diff --git a/.gitignore b/.gitignore index 514dcd6..4e0fd3d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,3 @@ logs/ data/doc/ data/test/ .vscode/ -com.* -cn.com.* diff --git a/JUSTTRUSTME.md b/JUSTTRUSTME.md new file mode 100644 index 0000000..cfa809e --- /dev/null +++ b/JUSTTRUSTME.md @@ -0,0 +1,94 @@ +# Twitter实战测试 + +### 不使用just_trust_me情况下,抓包失败展示 +![抓包失败展示.png-443.6kB][2] + +### spawn方式启动just_trust_me.js +建议任何app都以spawn方式启动just_trust_me.js。不是建议!是必须,因为要在app完成初始化之前hook住所有的hook点,只有spawn模式下才能做到,attach时机太晚了。所以大家明白? +![启动just_trust_me.gif-2756.5kB][3] + + + +### 启动just_trust_me.js之后正常抓包展示 +久违的请求出来了!!!看!!! +![启动just_trust_me之后.gif-2307.9kB][4] + + +# 某头条资讯APP实战测试 + +### 启动just_trust_me.js +![WechatIMG417.png-1098.4kB][5] + +### 正常抓包展示 +![qutoutiao_result.gif-2120.1kB][6] + + +# 某买菜APP实战测试 + +### 启动just_trust_me.js +![WechatIMG420.png-1185.2kB][7] + +### 正常抓包展示 +![WechatIMG422.png-763.4kB][8] + + +#### 就是这么的简单粗暴,我们继续...... + +# 某汽车资讯APP实战测试 +### 启动just_trust_me.js +![111.png-1099.8kB][9] + +### 正常抓包展示 +![222.png-772.2kB][10] + +# 某国内免费小说APP实战测试 +### 启动just_trust_me.js +![333.png-1054.4kB][11] + +### 正常抓包展示 +![444.png-678.5kB][12] + +# 某宝购物APP +### 启动just_trust_me.js +![666.png-1151.4kB][13] + +### 正常抓包展示 +![555.png-879.3kB][14] + + +还要测吗?这个frida版本的just_trust_me怎么样?有些okhttp库在编译的时候混淆了,你可以通过[just_trust_me_okhttp_hook_finder.js](https://github.com/CreditTone/hooker/blob/master/js/just_trust_me_okhttp_hook_finder.js)把混淆类找回来。如何使用just_trust_me_okhttp_hook_finder请看《实现frida版的JustTrustMe(三)frida版JustTrustMe升级和混淆对抗》 + + +# 再次强调实现frida版JustTrustMe的意义 + +### 快速使用 + 不用我说了吧,大家上面都看到了。xposed有我frida这么洒脱吗?批量刷机都是问题吧?frida只要你手机root即可,这才叫活好不粘人!!! + +### frida的反检测性比xposed好很多 + xposed虽然有许多山寨版,在某些方面魔改可以对抗一些检测。但是依然是投入产出比不高,付出了那么多精力结果反检测性并没有实质性的上去。原因是xposed在设计的时候就走错了,等他发现基于替换zygote进程孵化容易被检测的时候已经晚了。所以xposed一直是错着往前!感兴趣的去看我《实现frida版的JustTrustMe(一)JustTrustMe官方源码学习》在开头讲的。 + +### 对抗混淆 + 我提供了[just_trust_me_okhttp_hook_finder.js](https://github.com/CreditTone/hooker/blob/master/com.twitter.android/just_trust_me_okhttp_hook_finder.js)使你可以快速定位到okhttp[3-4]的混淆点,让你可以快速对每个app进行定制。 + +### frida可定制性好 + frida方面你只要会js就可以修改[just_trust_me.js](https://github.com/CreditTone/hooker/blob/master/com.twitter.android/just_trust_me.js),而xposed要求你至少会基本的java、android开发和熟悉xposed框架api。 + +# 后记 +没有100%能把所有包都抓住,都要基于每个app的反抓包策略进行一些调整。还有包括双向认证,需要在抓包工具上配置客户端证书、还有要vpn转发的、还有走私有协议的是不能用httpproxy类的抓包工具来抓包的等等吧。但是我会尽最大可能,让大家在操作相对简单的情况下帮大家抓到包。节省大家一点点时间,工具的本质就是提效。懒人出科技就是这样! + + + + [1]: http://static.zybuluo.com/1273568669/0gjhwhbgg8yw6nyot1lgsfrw/WechatIMG415.png + [2]: http://static.zybuluo.com/1273568669/pakmqbet9bfkthi3tn1420dk/%E6%8A%93%E5%8C%85%E5%A4%B1%E8%B4%A5%E5%B1%95%E7%A4%BA.png + [3]: http://static.zybuluo.com/1273568669/ayyj2o1o6m9ndluea7p4uaal/%E5%90%AF%E5%8A%A8just_trust_me.gif + [4]: http://static.zybuluo.com/1273568669/vhrma5jzvzjyjtwit99ojxtn/%E5%90%AF%E5%8A%A8just_trust_me%E4%B9%8B%E5%90%8E.gif + [5]: http://static.zybuluo.com/1273568669/al811jvk2act05mhvyzpywp8/WechatIMG417.png + [6]: http://static.zybuluo.com/1273568669/xufqyh60yjfutidit3bbarf1/qutoutiao_result.gif + [7]: http://static.zybuluo.com/1273568669/agierqyhvh78b6vt2m25rs2a/WechatIMG420.png + [8]: http://static.zybuluo.com/1273568669/6xy4b84vnch2i1zjbfu5iqiy/WechatIMG422.png + [9]: http://static.zybuluo.com/1273568669/pmaug9o12xyaofd6kf7vl6z3/111.png + [10]: http://static.zybuluo.com/1273568669/xyf0czutgr1ho2vuxswe5tk9/222.png + [11]: http://static.zybuluo.com/1273568669/qvzlycuwjkfil7i0mz7765sh/333.png + [12]: http://static.zybuluo.com/1273568669/1e6ykjjfsqf8kr4u7d9jldff/444.png + [13]: http://static.zybuluo.com/1273568669/lnikbsd36yq6q8zfw3kp0b5g/666.png + [14]: http://static.zybuluo.com/1273568669/pt49hc31pjy8del9ssh9s2iv/555.png \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..29cac8c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021, guozhong + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index fc045ea..367b0c2 100644 --- a/README.md +++ b/README.md @@ -1,1129 +1,748 @@ -[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/credittone-hooker-badge.png)](https://mseep.ai/app/credittone-hooker) +### 欢迎使用hooker逆向工作台 -免责声明 -本项目的所有内容仅供学习与技术交流使用,旨在帮助开发者理解移动应用的结构和工作原理。 +hooker是一个基于frida实现的逆向工具包。为逆向开发人员提供统一化的脚本包管理方式、通杀脚本、自动化生成hook脚本、内存漫游探测activity和service、frida版JustTrustMe。 -本项目不包含任何针对特定应用的破解操作或侵权内容。 -针对某些app存在的简单解包技术内容在各大技术论坛网站均大量存在,并无对这些app造成实际损害 -本项目无意协助任何非法用途,包括但不限于绕过版权保护、修改应用功能或获取未经授权的数据。 -请确保在使用本项目工具时遵守相关法律法规,并仅用于个人学习或研究目的。 +# [hooker开源协议](https://github.com/CreditTone/hooker/blob/master/LICENSE.md) +https://github.com/CreditTone/hooker/blob/master/LICENSE.md -

简体中文 | English

+hooker使用[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)开源协议,协议核心规范如下 -# 欢迎使用hooker逆向工作台 -![GitHub stars](https://img.shields.io/github/stars/CreditTone/hooker?style=flat-square) -![GitHub forks](https://img.shields.io/github/forks/CreditTone/hooker?style=flat-square) -![GitHub code size](https://img.shields.io/github/languages/code-size/CreditTone/hooker?style=flat-square) -![Python](https://img.shields.io/badge/python-3.8.8-blue?style=flat-square) -![frida](https://img.shields.io/badge/frida-16.7.19-blue?style=flat-square) + 1.授权使用者免费使用个人专利 -hooker是一个基于frida实现的逆向工具包。旨在为安卓逆向开发人员提供一个舒适的命令行界面和一些常用的通杀脚本、自动化生成hook脚本、内存漫游探测activity和service、frida版JustTrustMe、boringssl unpinning全网app通杀 + 2.使用者必须放置协议说明 + 3.使用者需要对修改部分声明 + 4.禁止用作者的名号进行商业广告 + + 5.原作者不承担代码使用后风险 + + +本项目仅作为学习使用,不参与任何入侵、破解计算机信息系统的行为。如有影响您app信息安全的地方,请立即与我联系删除,谢谢!邮箱:1273568669@qq.com + + +### radar增强项目源码 +https://github.com/CreditTone/radar4hooker -为什么你需要hooker? ================= -* [1. frida版JustTrustMe,通杀全网APP,且作者一直在持续维护升级](#12-frida版JustTrustMe包括boringgssl) -* [2. 嵌入式webserver支持把App内部能力快速暴露成HTTP接口,便于做自动化和接口化](#7-嵌入式webserver) -* [3. 快捷设置socks5代理,无需额外安装socksdroid等三方app实现无感知代理](#11-快捷设置socks5无感代理) - +# 快速定位 +##### [自动化生成frida hook脚本](#j---生成指定类的hook脚本) + +##### [frida版just_trust_me](#11-just_trust_mejs) +##### [frida版just_trust_me实战测试报告](https://github.com/CreditTone/hooker/blob/master/JUSTTRUSTME.md) ---- +##### [disable_sslpinning](#7-disable_sslpinning) 目录 ================= -* [快速开始](#快速开始) +* [hooker和frida、objection有什么不同](#hooker和frida-objection有什么不同) +* [hooker环境部署](#环境部署) * [1. git clone项目](#1-git-clone项目) - * [2. 安装依赖](#2-安装python3依赖) - * [3. root手机usb连接](#3-root手机usb连接PC) - * [4. 启动hooker](#4-启动hooker) - * [5. 输入调试应用包名](#5-输入调试应用包名) - * [6. 查看help信息](#6-查看help信息) - * [7. 嵌入式webserver](#7-嵌入式webserver) - * [8. 自动化生成frida脚本](#8-自动化生成frida脚本) - * [9. 列出所有frida脚本](#9-列出应用目录所有frida脚本) - * [10. attach执行frida脚本](#10-attach执行frida脚本) - * [11. 快捷设置socks5无感代理](#11-快捷设置socks5无感代理) - * [12. frida版JustTrustMe](#12-frida版JustTrustMe包括boringgssl) - * [13. spawn执行frida脚本](#13-spawn执行frida脚本) - * [14. 取消代理设置](#14-取消代理设置) - * [15. 重启app](#15-重启app) - * [16. 获取uid和pid](#16-获取uid和pid) - * [17. pull文件](#17-pull文件) - * [18. r0capture](#18-r0capture) - * [19. hooker自动升级](#19-upgrade) -* [开发手机API接口](#开发手机api接口) -* [hooker原生提供的操作API](#hooker原生提供的操作api) -* [应用工作目录脚本](#应用工作目录脚本) - * [url.js](#urljs) - * [just_trust_me.js](#just_trust_mejs) - * [activity_events.js](#activity_eventsjs) - * [click.js](#clickjs) - * [android_ui.js](#android_uijs) - * [keystore_dump.js](#keystore_dumpjs) - * [edit_text.js](#edit_textjs) - * [hook_register_natives.js](#hook_register_nativesjs) - * [text_view.js](#text_viewjs) - * [ssl_log.js](#ssllogjs) - * [just_trust_me_for_ios.js](#just_trust_me_for_iosjs) - * [dump_dex.js](#dump_dexjs) - * [trace_init_proc.js](#trace_init_procjs) - * [hook_artmethod_register.js](#hook_artmethod_registerjs) - * [find_anit_frida_so.js](#find_anit_frida_sojs) - * [jni_method_trace.js](#hook_jni_method_tracejs) - * [replace_dlsym_get_pthread_create.js](#replace_dlsym_get_pthread_createjs) - * [find_boringssl_custom_verify_func.js](#find_boringssl_custom_verify_funcjs) - * [get_device_info.js](#get_device_infojs) - * [apk_shell_scanner.js](#apk_shell_scannerjs) - * [bypass_frida_svc_detect.js](#bypass_frida_svc_detectjs) - * [bypass_root_detect.js](#bypass_root_detectjs) - * [bypass_vpn_detect.js](#bypass_vpn_detectjs) - * [hook_encryption_algo.js](#hook_encryption_algojs) - * [hook_encryption_algo2.js](#hook_encryption_algo2js) - * [webview_enable_debug.js](#webview_enable_debugjs) -* [Windows安装WSL](#windows安装wsl) - * [1. 安装wsl ubuntn24.04](#1-安装wsl-ubuntn2404) - * [2. 进入wsl,配置代理](#2-进入wsl配置代理) - * [3. 安装python3.8和frida](#3-安装python38和frida) -* [自定义frida-server](#自定义frida-server) -* [hooker命令行快捷键](#hooker命令行快捷键) - - - -手机保证root,无需任何手动启动frida-server等一切配置,hooker会帮你搞定一切。x86架构的模拟器不兼容 - -# Mac/Linux配置hooker运行环境 - -Windows请先完成[WSL安装](#windows%E5%AE%89%E8%A3%85wsl),然后跳回到这里 + * [2. 安装依赖](#2-安装依赖) + * [3. 手机连接adb](#3-手机连接adb) + * [4. 手机开发环境部署](#4-手机开发环境部署) + * [5. 指定fridaserver端口的手机开发环境部署](#5-指定fridaserver端口的手机开发环境部署) + * [6. 部署之后手机的增强功能](#6-部署之后手机的增强功能) +* [快速开始](#快速开始) + * [1. 查看可调试进程](#1-查看可调试进程) + * [2. attach一个应用](#2-attach一个应用) + * [3. 应用工作目录](#3-应用工作目录) +* [应用工作目录的命令](#应用工作目录的命令) + * [1. hooking](#1-hooking) + * [2. attach](#2-attach) + * [3. spawn](#3-spawn) + * [4. objection](#4-objection) + * [5. xinitdeploy](#5-xinitdeploy) + * [6. kill](#6-kill) + * [7. disable_sslpinning](#7-disable_sslpinning) +* [应用工作目录的通杀脚本](#应用工作目录的通杀脚本) + * [1. url.js](#1-urljs) + * [2. activity_events.js](#2-activity_eventsjs) + * [3. click.js](#3-clickjs) + * [4. android_ui.js](#4-android_uijs) + * [5. keystore_dump.js](#5-keystore_dumpjs) + * [6. edit_text.js](#6-edit_textjs) + * [7. text_view.js](#7-text_viewjs) + * [8. ssl_log.js](#8-ssl_logjs) + * [9. object_store.js](#9-object_storejs) + * [10. hook_RN.js](#10-hook_RNjs) + * [11. just_trust_me.js](#11-just_trust_mejs) + * [12. just_trust_me_okhttp_hook_finder.js](#12-just_trust_me_okhttp_hook_finderjs) +* [hooker调试命令行](#hooker调试命令行) + * [a-打印Activity栈](#a---打印activity栈) + * [b-打印Service栈](#b---打印Service栈) + * [c-扫描指定Object](#c---扫描指定Object) + * [d-展开Object[]、List或Map](#d---%E5%B1%95%E5%BC%80objectlist%E6%88%96map) + * [v-以View方式查看对象](#v---以view的方式查看对象) + * [e-检测类在内存中是否存在](#e---检测类在内存中是否存在) + * [s-正则表达式扫描类](#s---正则表达式扫描类) + * [j-生成指定类的hook脚本](#j---生成指定类的hook脚本) + * [k-生成字符串hook脚本](#k---%E7%94%9F%E6%88%90%E5%AD%97%E7%AC%A6%E4%B8%B2hook%E8%84%9A%E6%9C%AC) +* [hooker高级应用](#hooker高级应用) + * [radar.dex](#radardex) + * [脚本的内置函数](#脚本的内置函数) + * [1. loadDexfile(dexfile)](#1-loaddexfiledexfile) + * [2. checkLoadDex(className,dexfile)](#2-checkloaddexclassname-dexfile) + * [3. loadXinitDexfile(dexfile)](#3-loadxinitdexfiledexfile) + * [4. loadXRadarDexfile()](#4-loadxradardexfile) + * [5. fastTojson(javaObject)](#5-fasttojsonjavaobject) + * [6. getPrettyString(javaObject)](#6-getprettystringjavaobject) + * [7. getField(javaObject, fieldName)](#7-getfieldjavaobject-fieldname) + * [8. storeObjectAndLog(javaObject)](#8-storeobjectandlogjavaobject) + * [原生ui自动化](#原生ui自动化) + * [1. startActivity(activityName)](#1-startactivityactivityname) + * [2. contextStartActivity(activityName)](#1-startactivityactivityname) + * [3. contextStartActivity(activityName)](#1-startactivityactivityname) + * [4. home()](#1-startactivityactivityname) + * [5. back()](#1-startactivityactivityname) + * [6. finishCurrentActivity()](#1-startactivityactivityname) + * [7. clickByText(text)](#1-startactivityactivityname) + * [8. clickById(id)](#1-startactivityactivityname) + * [9. hover(x,y,upStepLength)](#1-startactivityactivityname) + * [10. viewTree()](#1-startactivityactivityname) + * [远程frida支持](#远程frida支持) +* [更新教程](#更新教程) + * [1. clone最新项目](#1-clone最新项目) + * [2. 方式一覆盖核心文件到你的hooker](#2-方式一覆盖核心文件到你的hooker) + * [3. 方式二覆盖你的应用工作目录到最新hooker](#3-方式二覆盖你的应用工作目录到最新hooker) + +# hooker和frida、objection有什么不同 +- 职责不同:frida注重打造调试引擎、objection注重将frida的api简单封装一下让你好快速上手frida。而hooker是重新站在一个安卓应用开发和安卓逆向工程师的角度去打造的更加专业Android逆向工作台,重新定义了逆向android的工作方式。 +- 封装不同:frida是基于gumjs(V8)、C/C++封装的调试引擎,用于动态Hook跟踪、拦截和主动调用函数等。hooker是基于frida作为引擎和自己打造的Dex库(radar)调用Android Framework层代码完成的。 +- 交互方式不一样:frida和objection只有attach上才能操作各种指令,而hooker提供shell命令行交互式让你可以通过jadx进行动静结合分析。 +- 更注提供重Android逆向思路和线索:frida和objection没有对任何Android Freamwork层的hook和能主动调用代码点位进行封装,这使得难以有逆向思路。而hooker的几乎所有命令都是围绕Android Freamwork进行封装,让一个即使没有Android开发经验的人也能快速找到逆向分析思路。 +- hook脚本产出方式不一样:frida你需要先进行很多语法方面的学习,才能完成对各种类的各种方法进行frida脚本的编写。hooker不需要你了解frida语法细节,比如你只需通过j okhttp3.OkHttpClient:newCall 就可以生成一个hook okhttp3.OkHttpClient类的newCall方法的脚本, 即使对于任何一个被混淆的类操作也是如此。(你应该把更多的时间和精力放在逆向思路上,而不是熟悉某些语法上。) +- 提供操作原生AndroidUI功能:你可以./attach每个app目录下的android_ui.js脚本,它提供了通过ViewId、ViewText找到Android原生的View并点击,或者你想强制打开某个Activity(比如某个界面只有会员才能进入,这时候你就可以采用Android"原生代码"打开的方式)。 + +# 环境部署 +前言:hooker仅支持在Linux和MacOS下运行,并且现在和将来都不会支持windows操作系统!windows做开发是没有灵魂的! ### 1. git clone项目 ```shell -stephen@Mac:~$ git clone https://github.com/CreditTone/hooker.git -stephen@Mac:~$ cd hooker +stephen@ubuntu:~$ git clone https://github.com/CreditTone/hooker.git +stephen@ubuntu:~$ cd hooker +stephen@ubuntu:~$ ls +colorful.py com.mokee.aegis mobile-deploy.tar +com.alibaba.wireless com.sankuai.meituan org.mokee.lawnchair +com.changba com.sdiread.kt.ktandroid org.mokee.weatherservice +com.google.android.youtube com.smile.gifmaker __pycache__ +com.jifen.qukan com.ss.android.article.news radar.dex +com.jzg.jzgoto.phone com.ss.android.ugc.aweme README.md +com.koudai.weidian.buyer com.tencent.karaoke run_env.py +com.kugou.shiqutouch com.tencent.mm sogou.mobile.explorer +com.lululemon.shop hooker spider.py +com.meicai.mall hooker.py traceJNI +com.meitu.meipaimv js xapk +com.miui.screenrecorder mobile-deploy.sh xinitdeploy.py ``` -### 2. 安装python3依赖 +### 2. 安装依赖 ```shell -stephen@Mac:~/hooker$ pip3 install -r requirements.txt +stephen@ubuntu:~/hooker$ pip3 install -r requirements.txt ``` -### 3. root手机usb连接PC +### 3. 手机连接adb ```shell -stephen@Mac:~/hooker$ adb devices +stephen@ubuntu:~/hooker$ adb devices List of devices attached FA77C0301476 device ``` -### 4. 启动hooker - -这里注意,不要用绝对路径去执行,一定要cd到hooker目录下执行python3 hooker.py +### 4. 手机开发环境部署 +如果你的手机已经启动了frida-server,可以忽略这步。 -hooker启动后将收集所有可调试app的信息,字段含义如下 -- PID:当前app的主进程id,如果app没有启动则为0 -- APP:app的名称 -- IDENTIFIER:app的包名 -- EXIST_REVERSE_DIRECTORY:如果app曾经被调试过就是✅,从没被调试过就是❌ +注意:部分手机出现部署之后adb连不上的问题,那请使用deploy2.sh。 ```shell -stephen@Mac:~/hooker$ python3 hooker.py -hooker Let's enjoy reverse engineering together ------------------------------------------------------------------------------------------------ -PID APP IDENTIFIER EXIST_REVERSE_DIRECTORY -0 全球上网 com.miui.virtualsim ❌ -0 爱奇艺 com.qiyi.video ❌ -0 红手指云手机 com.redfinger.app ❌ -0 Reqable com.reqable.android ❌ -0 美团 com.sankuai.meituan ✅ -0 得物 com.shizhuang.duapp ❌ -0 某皮 cxm.shxpxx.sg ✅ -0 微博 com.sina.weibo ❌ -0 今日头条 com.ss.android.article.news ✅ -0 西瓜视频 com.ss.android.article.video ✅ -0 懂车帝 com.ss.android.auto ✅ -0 抖音火山版 com.ss.android.ugc.live ✅ -0 抖音精选 com.ss.android.yumme.video ❌ -0 淘宝 com.taobao.taobao ✅ -0 腾讯视频 com.tencent.qqlive ❌ -0 Termux com.termux ❌ -0 轻奢 com.tm.bachelorparty ✅ -0 WiFi ADB com.ttxapps.wifiadb ❌ -0 VMOS Pro com.vmos.pro ✅ -0 游戏中心 com.xiaomi.gamecenter ❌ -0 小米商城 com.xiaomi.shop ❌ -0 米家 com.xiaomi.smarthome ❌ -0 小米有品 com.xiaomi.youpin ✅ -0 小红书 com.xingin.xhs ✅ -0 运满满货主 com.xiwei.logistics.consignor ✅ -0 拼多多 com.xunmeng.pinduoduo ✅ -0 EnvCheck com.yimian.envcheck ✅ -0 check_env com.yuuki.check_env ❌ -0 TikTok com.zhiliaoapp.musically ❌ -0 XPrivacyLua eu.faircode.xlua ❌ -0 imToken im.token.app ❌ -0 SocksDroid net.typeblog.socks ❌ -0 F-Droid org.fdroid.fdroid ❌ -0 ProxyDroid org.proxydroid ❌ -3457 手机管家 com.miui.securitycenter ✅ -3509 优信拍 com.uxin.buyerphone ✅ -18780 抖音 com.ss.android.ugc.aweme ✅ -20174 应用商店 com.xiaomi.market ❌ -20913 设置 com.android.settings ❌ -30500 小爱同学 com.miui.voiceassist ❌ -32163 相机 com.android.camera ✅ -Please enter the identifier that needs to be reversed -hooker(Identifier): +#以piexl2为例 +stephen@ubuntu:~/hooker$ adb push mobile-deploy/ /sdcard/ +stephen@ubuntu:~/hooker$ adb shell #进入手机命令行界面 +sailfish:/ $ su #进入root权限命令行模式 +sailfish:/ $ sh /sdcard/mobile-deploy/deploy.sh +disable android firewall. +start frida-server +start network adb. +deploy successfull. +stephen@ubuntu:~/hooker$ #如果你看到你的adb命令被弹出来了,表示已经正常部署。 ``` +![部署演示](assets/hooker-deploy.gif) *** - -### 5. 输入调试应用包名 - -- 输入调试应用包名回车后,如果是第一次调试应用,hooker将创建应用目录,应用目录名称为应用的Identifier,用于存放所有js脚本和快捷命令。 - -- hooker将帮你检测当前app是否启动且在手机前台,如不在启动帮你启动,如不在前台帮你切到前台 - -- frida通杀脚本可以在hooker交互式命令行下用attach/spawn执行,也可以手动cd到应用目录用快捷命令或原生的frida命令执行。 - -- 你可以修改应用工作目录下任何脚本 - +### 5. 指定fridaserver端口的手机开发环境部署 ```shell -hooker(Identifier): cxm.shxpxx.sg -✅ App cxm.shxpxx.sg is already in the foreground -Creating working directory: cxm.shxpxx.sg -Generating frida shortcut command... -Generating built-in frida script... -pull /data/app/cxm.shxpxx.sg-L8zkrpFVICv0-hOrtmPPxA==/base.apk to cxm.shxpxx.sg/ShopeeSG_3.43.40.apk successful -Working directory create successful -just_trust_me.js empty.js keystore_dump.js -edit_text.js activity_events.js find_boringssl_custom_verify_func.js -ssl_log.js hook_register_natives.js click.js -get_device_info.js apk_shell_scanner.js dump_dex.js -object_store.js hook_artmethod_register.js replace_dlsym_get_pthread_create.js -just_trust_me_for_ios.js trace_initproc.js android_ui.js -jni_method_trace.js url.js just_trust_me_okhttp_hook_finder_for_android.js -text_view.js find_anit_frida_so.js -某皮 > +stephen@ubuntu:~/hooker$ adb shell #进入手机命令行界面 +sailfish:/ $ su #进入root权限命令行模式 +sailfish:/ $ sh /sdcard/mobile-deploy/deploy.sh 6666 #deploy.sh启动失败的同样可以尝试deploy2.sh +disable android firewall. +set firda_server_bind_port to 6666 +start frida-server +start network adb. +deploy successfull. +stephen@ubuntu:~/hooker$ #如果你看到你的adb命令被弹出来了,表示已经正常部署。 ``` -![hooker_enter_debug.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_enter_debug.gif) *** +注意:自定义frida server端口的开发环境必须走host:post的方式调试,因为usb默认找27042端口。所以请务必[更改本地.hooker_driver文件](#远程frida支持),否则hooker无法正常工作。 - - -### 6. 查看help信息 - -在使用hooker过程中,如不记得命令,可随时调出help查看操作手册。 - -```shell -某皮 > help -h, help show this help message -a, activitys show the activity stack -s, services show the service stack -o, object [object_id] show object info by object_id -v, view [view_id] show view info by view_id of view -gs, generatescript [class_name:method_name] specify the class name and method name to generate a frida hook java script file. For example: generatescript - okhttp3.Request$Builder:addHeader -p, proxy [socks5_proxy_server] set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998 -up, unproxy remove socks5 proxy for this app -trust, justtrustme quickly spawn just_trust_me.js script to kill all ssl pinning -ls list all the frida scripts of the current app -attach [script_file_name] quickly execute a frida script, similar to executing the command "frida -U com.example.app -l xxx.js". For example: attach url.js -spawn [script_file_name] quickly spawn a frida script, similar to executing the command "frida -U -f -n com.example.app -l xxx.js". For example: spawn - just_trust_me.js -restart restart this app -pid get pid of this app main process -uid get pid of this app -exit return to the previous level -某皮 > -``` -![hooker_help.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_help.gif) +### 6. 部署之后手机的增强功能 +- 1.关闭iptables防火墙,解决部分手机默认防火墙开启的问题 +- 2.启动frida-server,如果你的手机是arm64他将优先启动arm64位的frida-server +- 3.在/data/mobile-deploy目录生成tools_env.rc 当你有内网穿透和网络服务转发、编辑文件、检测网络方面的需求时可以执行source /data/mobile-deploy/tools_env.rc,它将临时生成vi、telnet、frpc、tcpforward、ll命令以便你进行更便捷的开发,如图 +![部署演示](assets/tools_env.gif) *** - - - -### 7. 嵌入式webserver - -hooker 支持在手机中注入一个轻量级 webserver。启动后会在目标 App 进程内开启一个 HTTP 服务,默认端口是 `8080`。这个服务既可以暴露 patch 项目里自定义的 controller,也会自动挂载一组内置调试接口。 - -- 启动内置 webserver - -```shell -某音火山版 > webserver start -Http server port: 8080 -Http server: http://10.112.101.249:8080 -``` - -启动内置 webserver 后,浏览器打开首页即可看到当前已注册的 API 列表。常用能力包括: - -- 服务管理:`/` 查看欢迎页和接口清单,`/stop` 停止当前 webserver。 -- UI 自动化:`/hooker/ui/...` 提供点击控件、按文本点击、按坐标点击、设置输入框文本、触发返回/Home、启动 Activity、获取屏幕信息、翻页、滚动 RecyclerView、尝试关闭弹窗等能力,适合做半自动化操作和页面联调。 -- 页面结构导出:`/hooker/uiauto/dump`、`/hooker/uiauto/window_dump.xml`、`/hooker/uiauto/window_dump.json` 可以导出当前窗口层级,方便定位控件和分析页面结构。 -- 截图能力:`/hooker/screencap/screenshot` 可直接调用系统 `screencap` 截图;`/hooker/mediaprojection/...` 支持申请 MediaProjection 权限并抓取整屏 PNG,更适合做远程观察和自动化闭环。 -- App 信息读取:`/hooker/appinfo` 可读取包名、版本、组件、权限、签名和目录信息;`/hooker/appinfo/shared_prefs`、`/hooker/appinfo/databases`、`/hooker/appinfo/read_table` 可以直接查看 `shared_prefs`、数据库结构和表数据。 -- 类/对象辅助调用:`/hooker/classhelper/invoke_static_method`、`/hooker/classhelper/invoke_method` 支持通过 HTTP 直接调用静态方法或已缓存对象的方法,便于快速验证算法、补环境或调试业务逻辑。 -- 文件回传:`/file?filename=...` 可以把绝对路径文件或 webserver 缓存目录里的文件直接通过 HTTP 返回。 -- MCP 风格 UI 工具:`/hooker/mcp/ui/tools` 和 `/hooker/mcp/ui/call` 把常用 UI 操作封装成统一工具接口,便于外部脚本或 Agent 通过 HTTP 驱动当前 App。 - - -- 7.1 启动自定义 webserver - -给定一个patch工程的jar包,将爬虫接口启动为webserver。 - -```shell -某宝 > webserver start taxbax-patch.jar -Converting taxbax-patch.jar to taxbax-patch.dex... -Successfully converted to taxbax-patch.dex (7160 bytes) -push file OK /data/user/0/com.taxbax.taxbax/hooker_server.dex -Http server port: 2026 -Http server: http://10.112.101.249:2026 -``` - -这里的 `taxbax-patch.jar` 可以理解成“运行在目标 App 进程里的业务插件”。`hooker` 会先把 jar 转成 dex,再注入到目标进程中,扫描其中带注解的类,然后把这些类注册成 HTTP 路由。 - -- 7.2 自定义 webserver 适合做这类事情: - -- 暴露目标 App 内部已经存在的业务能力,比如搜索、评论、详情页、签名、加解密、用户资料、直播接口等。 -- 直接复用目标 App 自己的登录态、网络栈、环境参数和对象实例,避免在外部重复补协议。 -- 把异步回调、Observable、Listener、页面对象调用收敛成一个同步 HTTP 接口,对外统一返回 JSON 或文本。 - -你可以像开发springboot一样优雅的开发嵌入式的httpserver,并调用目标app的任何java代码而无需像有些xposed插件一样TMD的反射、反射、再反射,跟SB一样😊 - -- 用 `@HookerWebServerConfiguration(port = 2026)` 指定端口,不写时默认走 `8080`。 -- 用 `@HookerController("/taobao")`、`@HookerController("/douyin")` 这类注解定义业务前缀。 -- 用 `@HookerRequestMapping(path = "/getProductDetail")` 之类的注解暴露具体接口。 -- 用 `@HookerRequestParam`、`@HookerRequestPostJson` 接收查询参数和 POST JSON。 - - -具体开发文档,将在https://github.com/CreditTone/radar4hooker详细介绍 - - -- 7.3 webserver 持久化 - -如果你已经为某个 App 开发好了 patch 工程,并且需要在多台设备上长期部署,那么 `frida + hooker` 的临时注入方式会比较重。针对这种场景,作者提供了 Xposed 插件 `HookerServer`: - -`https://github.com/CreditTone/HookerServer` - -只要手机支持 Xposed/LSPosed,就可以借助这个插件把 webserver 持久化到目标 App 中。 - -操作步骤: - -1. 从 `https://github.com/CreditTone/HookerServer/releases` 下载最新 APK。 -2. 把 `patch.dex` 推送到 `/data/user/0/{package}/hooker_server.dex`。 -3. 在 Xposed/LSPosed 中启用 `HookerServer`,并勾选目标 App。 -4. 重启 App,webserver 即可自动启动。 - -其中 `patch.dex` 可以在第一次部署 `patch.jar` 后获得,hooker 会自动完成 jar 转 dex,并把生成结果放到目标应用的工作目录中。 - - +- 4.启动网络adb,即你可以直接通过远程adb调试手机。例如:adb connect 192.168.0.105 +![部署演示](assets/remote_adb.gif) *** +# 快速开始 -### 8. 自动化生成frida脚本 -自动化生成脚本是hooker的杀器。虽然现在AI大模型也可以写,但是我们离内存近,更快,也不需要联网。生成的脚本自带打印堆栈等信息,和一些你可能需要的扩展方法。 -另外在生成脚本的过程中,命令行类名、方法名提示也可以当作搜索使用,能通过关键词快速搜索定位类方法。hooker搜索类比jadx快很多,不信就试试...... - -![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg) -- Command语法:gs, generatescript [class_name:method_name] - - -- 8.1 生成指定方法的frida hook脚本: -gs okhttp3.Request$Builder:addHeader,参数部分(String, String)不是必须写的 - +### 1. 查看可调试进程 ```shell -某信拍 > gs okhttp3.Request$Builder:addHeader(String, String) -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder.addHeader.js -某信拍 > -``` - -```js -//cat okhttp3.Request.Builder.addHeader.js -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_method_addHeader_2grl = okhttp3_Request_Builder_clz.addHeader.overload('java.lang.String', 'java.lang.String'); - okhttp3_Request_Builder_clz_method_addHeader_2grl.implementation = function(string, string_x2) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.addHeader(java.lang.String,java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_addHeader_2grl.call(this, string, string_x2); - console.log("header name:" + string + " header value:" + string_x2); - printBeat(beat); - return ret; - }; -}); +stephen@ubuntu:~/hooker$ ./hooker + PID Name Identifier +----- ----------------------------- ------------------------------------------------------------- + 2857 Android Auto com.google.android.projection.gearhead + 1779 Android Services Library com.google.android.ext.services + 929 Android 系统 android + 5073 Carrier Services com.google.android.ims +11051 Device Health Services com.google.android.apps.turbo + 2913 Device Personalization S… com.google.android.as + 2522 Google com.google.android.googlequicksearchbox +15189 Google Play 商店 com.android.vending + 2101 Google Play 服务 com.google.android.gms + 2833 Google VR 服务 com.google.vr.vrcore + 7710 Google 服务框架 com.google.android.gsf + 2546 NFC服务 com.android.nfc + 929 NetworkStack com.android.networkstack.inprocess + 929 一体化位置信息 com.android.location.fused +14468 云端硬盘 com.google.android.apps.docs +14403 信息 com.google.android.apps.messaging +12073 存储已屏蔽的号码 com.android.providers.blockednumber + 1574 实时数据壁纸 com.ustwo.lwp +15637 抖音 com.ss.android.ugc.aweme + 2480 搜狗输入法 com.sohu.inputmethod.sogou +12073 用户字典 com.android.providers.userdictionary +13362 电话 com.google.android.dialer + 1704 电话和短信存储 com.android.providers.telephony + 1704 电话服务 com.android.phone +11818 知乎 com.zhihu.android + 1451 系统界面 com.android.systemui +12424 紧急警报 com.android.cellbroadcastreceiver +12073 联系人存储 com.android.providers.contacts + 1431 蓝牙 com.android.bluetooth + 929 设置存储 com.android.providers.settings +10149 运营商设置 com.google.android.wfcactivation +14376 通讯录 com.google.android.contacts + 929 通话管理 com.android.server.telecom +14807 阿里巴巴 com.alibaba.wireless +Enter the need to attach package. +: ``` -*** - - - -- 8.2 生成指定类的所有成员方法的frida hook脚本: -gs okhttp3.Request$Builder +### 2. attach一个应用 ```shell -某信拍 > generatescript okhttp3.Request$Builder -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder.allfunc.js +stephen@ubuntu:~/hooker$ ./hooker + PID Name Identifier +----- ----------------------------- ------------------------------------------------------------- +15637 抖音 com.ss.android.ugc.aweme + 2480 搜狗输入法 com.sohu.inputmethod.sogou +12073 用户字典 com.android.providers.userdictionary +13362 电话 com.google.android.dialer + 1704 电话和短信存储 com.android.providers.telephony + 1704 电话服务 com.android.phone +11818 知乎 com.zhihu.android + 1451 系统界面 com.android.systemui +12424 紧急警报 com.android.cellbroadcastreceiver +12073 联系人存储 com.android.providers.contacts + 1431 蓝牙 com.android.bluetooth + 929 设置存储 com.android.providers.settings +10149 运营商设置 com.google.android.wfcactivation +14376 通讯录 com.google.android.contacts + 929 通话管理 com.android.server.telecom +14807 阿里巴巴 com.alibaba.wireless +Enter the need to attach package. +: com.ss.android.ugc.aweme #在此处输入进程的Identifier即可调试应用 +It's com.ss.android.ugc.aweme that you have attached app. +-------------------------------------------------- +Please enter e, s, j, c or ex command. +a: Discovering activities. +b: Discovering services. +c: Discovering object. eg:'c {objectId}' +d: Object2Explain. eg:'d {objectId}' +v: Discovering view. eg:'v {viewId}' +e: Determines whether a class exists. eg:'e android.app.Application' +s: Discovering classes by a class'regex. eg:'s com.tencent.mm.Message.*' +t: Discovering offspring classes by a class'name. eg:'t com.tencent.mm.BasicActivity' +j: Generating hooked js. eg:'j okhttp3.Request$Builder:build' +k: Generating hooked the string generation js with a keyword. eg:'k {YourKeyword}' +l: Generating hooked the param generation js with a param keyword. eg:'l {YourKeyword}' +m: Discovering so module. +ex: Exit to the upper layer. eg:'ex' +: ``` -*** - -```js -//cat okhttp3.Request.Builder.allfunc.js -//okhttp3.Request$Builder -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_method_header_ng3n = okhttp3_Request_Builder_clz.header.overload('java.lang.String', 'java.lang.String'); - okhttp3_Request_Builder_clz_method_header_ng3n.implementation = function(string, string_x2) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.header(java.lang.String,java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_header_ng3n.call(this, string, string_x2); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_cacheControl_q8q5 = okhttp3_Request_Builder_clz.cacheControl.overload('okhttp3.CacheControl'); - okhttp3_Request_Builder_clz_method_cacheControl_q8q5.implementation = function(cacheControl) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.cacheControl(okhttp3.CacheControl)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_cacheControl_q8q5.call(this, cacheControl); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_method_bjk9 = okhttp3_Request_Builder_clz.method.overload('java.lang.String', 'okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_method_bjk9.implementation = function(string, requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.method(java.lang.String,okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_method_bjk9.call(this, string, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_head_a5nq = okhttp3_Request_Builder_clz.head.overload(); - okhttp3_Request_Builder_clz_method_head_a5nq.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.head()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_head_a5nq.call(this); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_headers_to5i = okhttp3_Request_Builder_clz.headers.overload('okhttp3.Headers'); - okhttp3_Request_Builder_clz_method_headers_to5i.implementation = function(headers) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.headers(okhttp3.Headers)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_headers_to5i.call(this, headers); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_post_heaq = okhttp3_Request_Builder_clz.post.overload('okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_post_heaq.implementation = function(requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.post(okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_post_heaq.call(this, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_build_rmqx = okhttp3_Request_Builder_clz.build.overload(); - okhttp3_Request_Builder_clz_method_build_rmqx.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request okhttp3.Request$Builder.build()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_build_rmqx.call(this); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_patch_hp9u = okhttp3_Request_Builder_clz.patch.overload('okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_patch_hp9u.implementation = function(requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.patch(okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_patch_hp9u.call(this, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_url_0owi = okhttp3_Request_Builder_clz.url.overload('java.lang.String'); - okhttp3_Request_Builder_clz_method_url_0owi.implementation = function(string) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_url_0owi.call(this, string); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_removeHeader_uzb9 = okhttp3_Request_Builder_clz.removeHeader.overload('java.lang.String'); - okhttp3_Request_Builder_clz_method_removeHeader_uzb9.implementation = function(string) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.removeHeader(java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_removeHeader_uzb9.call(this, string); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_url_ykbd = okhttp3_Request_Builder_clz.url.overload('java.net.URL'); - okhttp3_Request_Builder_clz_method_url_ykbd.implementation = function(url) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.net.URL)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_url_ykbd.call(this, url); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_delete_dqyl = okhttp3_Request_Builder_clz.delete.overload(); - okhttp3_Request_Builder_clz_method_delete_dqyl.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.delete()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_delete_dqyl.call(this); - printBeat(beat); - return ret; - }; - //.......省略N行代码 -``` -*** - +##### 提示1: 第一次调试你的应用时hooker将在当前目录生成以进程Identifier命名的应用专有工作目录,并初始化生成一些你可能会用到的通杀脚本。 +##### 提示2: 成功attach一个应用时,命令将pause在等待输入调试指令的阶段。pause状态下使用命令进行高级调试请直接跳到[hooker调试命令行](#hooker调试命令行) +##### 提示3: attach时hooker会将[radar.dex](#radardex)文件拷贝到手机的/data/user/0/{package}/目录下 +![](assets/hooker-attach.gif) -- 8.3 生成指定类的构造方法的frida hook脚本: -gs okhttp3.Request$Builder:_ 或者gs okhttp3.Request$Builder:\ +### 3. 应用工作目录 +应用工作目录的意义在于提供一个的地方存放和管理frida脚本和快捷命令。hooker在你第一次调试应用时会创建应用工作目录,并初始化一些通杀脚本和快捷命令。 ```shell -某信拍 > gs okhttp3.Request$Builder:() -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder._init.js +stephen@ubuntu:~/hooker/com.ss.android.ugc.aweme$ ls -l +total 784 +-rw-rw-r-- 1 stephen stephen 7662 3月 16 20:55 activity_events.js +-rw-rw-r-- 1 stephen stephen 5790 3月 16 20:55 android_ui.js +-rwxrwxrwx 1 stephen stephen 102 8月 3 2020 attach +-rw-rw-r-- 1 stephen stephen 2242 8月 3 2020 click.js +-rw-rw-r-- 1 stephen stephen 12687 3月 23 22:23 com.bytedance.frameworks.core.encrypt.RequestEncryptUtils.js +-rw-rw-r-- 1 stephen stephen 4322 8月 3 2020 edit_text.js +-rwxrwxrwx 1 stephen stephen 159 8月 3 2020 hooking +-rwxrwxrwx 1 stephen stephen 101 8月 3 2020 kill +-rw-rw-r-- 1 stephen stephen 709448 3月 18 22:11 log +-rwxrwxr-x 1 stephen stephen 99 3月 16 20:55 objection +-rw-rw-r-- 1 stephen stephen 1226 3月 16 20:55 object_store.js +-rw-rw-r-- 1 stephen stephen 2553 3月 16 20:55 spider.py +-rw-rw-r-- 1 stephen stephen 2371 8月 3 2020 text_view.js +-rw-rw-r-- 1 stephen stephen 4789 3月 16 20:55 url.js +drwxrwxr-x 2 stephen stephen 4096 3月 25 21:21 xinit +-rwxrwxr-x 1 stephen stephen 5552 3月 16 20:55 xinitdeploy ``` -```js -//cat okhttp3.Request.Builder._init.js -//okhttp3.Request$Builder:() -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_init_uw3i = okhttp3_Request_Builder_clz.$init.overload(); - okhttp3_Request_Builder_clz_init_uw3i.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder()'; - var beat = newMethodBeat(beatText, executor); - var returnObj = okhttp3_Request_Builder_clz_init_uw3i.call(this); - printBeat(beat); - return returnObj; - }; - var okhttp3_Request_Builder_clz_init_e58t = okhttp3_Request_Builder_clz.$init.overload('okhttp3.Request'); - okhttp3_Request_Builder_clz_init_e58t.implementation = function(v0) { - var executor = this.hashCode(); - var beatText = 'okhttp3.Request$Builder(okhttp3.Request)'; - var beat = newMethodBeat(beatText, executor); - var returnObj = okhttp3_Request_Builder_clz_init_e58t.call(this, v0); - printBeat(beat); - return returnObj; - }; -}); -``` -*** - - - +# 应用工作目录的命令 -### 9. 列出应用目录所有frida脚本 - -- 查看应用目录下所有的脚本,这里有hooker给你生成的通杀脚本,也有您生成的指定hook脚本,您可以修改定制。 - -- 如果您有自定义的脚本放到应用目录下,ls命令可以将脚本名称刷入命令行提示缓存 +### 1. hooking +hooking命令需要在后面跟一个脚本文件名作为参数,例如 ./hooking url.js。hooking实际上是在传统frida attach的基础上增加了将hook输出信息持久化到log文件中,比如当你hook一个调用非常频繁的函数比如某些字符串生成,输出的日志量无法短时间去全面,这时候你可以用文本编辑器打开log文件慢慢分析。以抖音工作目录为例,hooking实现如下 ```shell -某皮 > ls -just_trust_me.js empty.js keystore_dump.js -okhttp3.Request.Builder.addHeader.js edit_text.js activity_events.js -find_boringssl_custom_verify_func.js ssl_log.js hook_register_natives.js -click.js get_device_info.js apk_shell_scanner.js -dump_dex.js object_store.js hook_artmethod_register.js -replace_dlsym_get_pthread_create.js just_trust_me_for_ios.js trace_initproc.js -android_ui.js jni_method_trace.js url.js -just_trust_me_okhttp_hook_finder_for_android.js text_view.js find_anit_frida_so.js -某皮 > +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.ss.android.ugc.aweme | tee -ai log ``` -*** +![](assets/hooking_log.gif) -### 10. attach执行frida脚本 - -在hooker下执行frida脚本,您只需要输入attach【空格】脚本名称会自动弹出提示。上下选择需要的脚本按tab键即可自动输入全名称。 -这是hooker在追求极致的工匠精神,帮助您从开波音737到开空客320的跳跃。 - -> **日志同步**:`attach` / `frida` 执行脚本时,Frida 脚本的 `send()` 输出会自动同步保存到应用工作目录下的同名 `.log` 文件。例如 `attach url.js` 会生成 `url.log`,方便喂给 AI 分析。 +### 2. attach +attach同hooking类似,但是相比hooking少了日志持久化功能,这才是原生frida attach的命令。例如:./attach android_ui.js。以抖音工作目录为例,attach实现如下 ```shell -某信拍 > attach url.js -Frida output logging -> com.uxin.buyerphone/url.log -------------startFlag:0755liv1,objectHash:-915348569,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836814835--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836814832&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -public java.net.URL(String) - at java.net.URL.(Native Method) - at com.wxbx.wmda.e.b.a(SourceFile:5) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:0755liv1,usedtime:1--------------- - -------------startFlag:1ps6go99,objectHash:-237375819,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815192--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -public java.net.URL(String) - at java.net.URL.(Native Method) - at com.android.okhttp.HttpUrl.url(HttpUrl.java:327) - at com.android.okhttp.Request.url(Request.java:53) - at com.android.okhttp.Request$Builder.build(Native Method) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258) - at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) - at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26) - at com.wxbx.wmda.e.b.a(SourceFile:14) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:1ps6go99,usedtime:0--------------- -// 这里省略无数日志............. -------------startFlag:i7osxvjl,objectHash:134280600,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815193--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -com.android.okhttp.Request.Builder.build() - at com.android.okhttp.Request$Builder.build(Native Method) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258) - at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) - at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26) - at com.wxbx.wmda.e.b.a(SourceFile:14) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:i7osxvjl,usedtime:1--------------- -// 这里省略无数日志............. +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.ss.android.ugc.aweme ``` -*** - -### 11. 快捷设置socks5无感代理 - -通过iptables链路层转发包实现一键设置代理,优势是APP完全无感知被代理。推荐使用charles的socks5 - -设置代理后必须主动去[关闭代理](#14-取消代理设置),代理不会自动取消 +### 3. spawn +以spawn模式启动脚本。某些方法在应用启动的时候就初始化了,想要hook这些方法就必须以spawn模式启动了。默认是--no-pause,即不需要手动resume恢复应用。如果需要非--no-pause,请手动编辑spawn文件去除。以抖音工作目录为例,spawn实现如下 ```shell -某音 > proxy socks5://10.112.99.11:9998 -proxy socks5://10.112.99.11:9998 OK -某音 > +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER --no-pause -f com.ss.android.ugc.aweme -l $1 ``` -*** - -### 12. frida版JustTrustMe(包括boringgssl) -关掉SSL证书校验 +### 4. objection +快捷执行objection调试命令,执行./objection即可。以抖音工作目录为例,objection实现如下 ```shell -某音 > justtrustme -Package name: com.ss.xxxx.xxx.aweme -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -// 这里省略无数日志............. +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.ss.android.ugc.aweme explore ``` -*** +![](assets/objection.gif) +### 5. xinitdeploy +xinitdeploy是用于部署资源的命令,它会把xinit目录下所放的文件拷贝到手机上/data/user/0/{packageName}/xinit/。同时保证资源文件的user/group权限和app进程是同一个临时用户。 +![](assets/xinitdeploy.gif) +![](assets/xinit_files.png) -### 13. spawn执行frida脚本 - -> **日志同步**:`spawn` / `fridaf` 执行脚本时,Frida 脚本的 `send()` 输出同样会自动同步保存到同名 `.log` 文件。例如 `spawn just_trust_me.js` 会生成 `just_trust_me.log`。 +### 6. kill +如果你想重启app,先执行./kill会杀掉应用的主进程和所有子进程。作为一个Andrioid应用开发工程师出身,然后干到后台,接着干到爬虫,现在干到逆向的我必须告诉你:每个手机厂商都会实现一个自己的“内存清理”工具效果不一定好,且可能app本身也有保活机制。所以不建议你通过操作手机滑动进程列表来杀——有可能杀不干净。以抖音工作目录为例,kill实现如下: ```shell -某信拍 > spawn just_trust_me.js -Frida output logging -> com.uxin.buyerphone/just_trust_me.log -Package name: com.xxx.buyxxphone -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -// 这里省略无数日志............. +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.ss.android.ugc.aweme ``` -*** - +![](assets/kill.gif) -### 14. 取消代理设置 -一键取消代理 - -```shell -某音 > unproxy -unproxy OK -某音 > -``` -*** +### 7. disable_sslpinning +快速关闭ssl pinning,此命令基于objection源码实现和[frida版just_trust_me](#11-just_trust_mejs)互补。当just_trust_me失效的时候,可以使用disable_sslpinning。反之,使用[just_trust_me](#11-just_trust_mejs)。 +![](assets/disable_sslpinning.gif) -### 15. 重启app +# 应用工作目录的通杀脚本 +### 1. url.js +需要跟踪url生成时可执行./hooking url.js +![](assets/hooking_url.gif) -```shell -某信拍 > restart -restarts com.xxx.buyxxphone -``` -*** +### 2. activity_events.js +当你需要跟踪start某个Activity启动时可执行,获取startActivity的intent信息和调用堆栈。/hooking activity_events.js +![](assets/activity_events.gif) -### 16. 获取uid和pid +### 3. click.js +跟踪点击事件时可执行,并获取被点击View的真实VClass(很重要)。获取到了Class,你就可以在jadx找到这个View绑定事件代码。多一种办法定位到关键逻辑不好吗?一定要靠分析网络请求吗?条条大路通罗马,不一定非从网络库分析! /hooking click.js +![](assets/click.gif) -```shell -某信拍 > uid -10189 -某信拍 > pid -3509 -``` +### 4. android_ui.js +封装一些操作原生Android UI的函数。如startActivity(activityName)、home()、back()、finishCurrentActivity()、clickByText(text) 等等,命令使用得用attach './attach android_ui.js' 原理是借助radar.dex作为代理操作Android原生View。(tag) +![](assets/android_ui.gif) *** +### 5. keystore_dump.js +在https双向认证的情况下,dump客户端证书为p12。存储位置:/data/user/0/{packagename}/client_keystore_{nowtime}.p12 证书密码: hooker。原理是hook java.security.KeyStore的getPrivateKey和getCertificate方法,因为客户端向服务发送证书必调这个方法。强烈建议keystore_dump.js用spawn模式启动,启动命令为 ./spawn keystore_dump.js 。下面是某app双向认证dump客户端证书过程 +![](assets/https_bothway_01.png) +![](assets/https_bothway_02.png) +![](assets/https_bothway_03.png) +![](assets/https_bothway_04.png) -### 17. pull文件 +### 6. edit_text.js +跟踪获取Editview的getText()事件,并获取Editview的真实Class(很重要)。Editview一般绑定Search Action的实现代码,如果你抓取“搜索”接口。那么这个一定可以帮助你定位发送搜索请求的相关代码。多一种办法定位到关键逻辑不好吗?一定要靠分析网络请求吗?条条大路通罗马,不一定非从网络库分析! +![](assets/edit_text.png) -快捷拉取文件到本地应用工作目录 +### 7. text_view.js +跟踪TextView的setText和getText,获取TextView的真实Class。一般setText的堆栈信息会带出业务层的数据model处理逻辑,进而进一步分析到业务层数据bean封装类。 +![](assets/text_view.png) -```shell -转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libmsaoaidsec.so -pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libmsaoaidsec.so to com.xxx.zhuanmou/libmsaoaidsec.so successful -转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libsignLib.so -pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libsignLib.so to com.xxx.zhuanmou/libsignLib.so successful -转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libweconvert.so -pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libweconvert.so to com.xxx.zhuanmou/libweconvert.so successful -``` -*** +### 8. ssl_log.js +在native层跟踪ssl握手并记录CLIENT RANDOM,tcpdump出来的链路层pacp里面的TLS包可以用CLIENT RANDOM记录文件解出来,将在高级篇讲解使用步骤。 +### 9. object_store.js +操作ObjectId标识的对象,根据自身分析情况可进行特定的序列化打印、操作对象的私有成员变量。 +实践文章:https://bbs.pediy.com/thread-267245.htm +### 10. hook_RN.js +对于动态注册的native函数,我们需要用hook_RN.js来分析。强烈建议hook_RN.js用spawn模式启动,启动命令为 ./spawn hook_RN.js +![](assets/hook_RN.gif) -### 18. r0capture -hooker集成了r0capture,抓包产生的pcap文件保存在{应用包名}/r0capture_ssl.pcap路径下,如酷安:com.coolapk.market/r0capture_ssl.pcap +### 11. just_trust_me.js +frida版本的just_trust_me。如果你需要bypass sslpinning请执行./spawn just_trust_me.js -```shell -JqgvBRe45o4QLyGguX+eVDoN0CPLTcPXqRVBhh13z2PTch2W7Hgv\/xlp4x2v\/QemWXrjWuifc2el1gzK1+8YPW+1NyTFCC8P10+zpCAPRgBwxpjKp4ecSQngU32yY2daIbaEwj0fvAg12VZNCdtI8jtpGtgds5xe61cihcBaYg\/CTvUIEylZqE6cbWsbuiBf7OuJLAnofXi3JtUaD+kJxFQ4fsZOTxhpZqANHIVv17GPcG4CoJEMws8UzawN3xPMqVYdzv+bpAnbDRhZy6LsVxS5S6yYtrawQdroJqVfsaLXlzgTBQe6RVPYqWG38QKJ1cuOGttk0ukigGIce3QAUcJl0c3fsi973ydYnSY60PBSKumqZAFh4VM0jk5tmRUtZlrqfQDVmfgIaocPQ=="} -2025-06-19 11:35:59.794 | INFO | __main__:r0capture_on_message:939 - java.lang.Throwable - at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream.write(Native Method) - at com.android.okhttp.okio.Okio$1.write(Okio.java:76) - at com.android.okhttp.okio.AsyncTimeout$1.write(AsyncTimeout.java:155) - at com.android.okhttp.okio.RealBufferedSink.flush(RealBufferedSink.java:221) - at com.android.okhttp.internal.http.Http1xStream.finishRequest(Http1xStream.java:161) - at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:735) - at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:609) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:471) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:407) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:244) - at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210) - at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:26) - at com.tencent.msdk.dns.core.rest.share.a.a(AbsHttpDns.java:23) - at com.tencent.msdk.dns.core.k$a.run(LookupHelper.java:24) - at com.tencent.msdk.dns.core.c$b.run(CountDownManager.java:3) - at com.tencent.msdk.dns.core.c$a.run(CountDownManager.java:3) - at com.tencent.msdk.dns.base.executor.DnsExecutors$a.run(DnsExecutors.java:38) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) - at java.lang.Thread.run(Thread.java:764) - -CTRL + C to stop > -Interrupting -flushing com.coolapk.market/r0capture_ssl.pcap successful -r0capture.js detach successful -Restarting 酷安 Please wait for a few seconds -``` -*** +下面以Twitter为例,启动just_trust_me.js +启动演示 +![](assets/strat_just_trust_me.gif) +抓包效果演示 +![](assets/just_trust_me_show.gif) -### 19. upgrade -hooker更新频繁,平均周更约10次。upgrade帮助您随时同步最新代码和相关文件到本地。 +### 12. just_trust_me_okhttp_hook_finder.js +为配合just_trust_me.js在okhttp混淆场景下辅助你找到混淆点 +jadx打开某资讯apk发现okhttp3如下 +![](assets/qutoutiao.png) +执行./hooking just_trust_me_okhttp_hook_finder.js命令扫描hook点 ```shell -MacBook-Pro-32G-2T:hooker stephen256$ python3 hooker.py upgrade -Upgrading hooker -Repository updated with 'git pull'. -Updating mobile-deploy/libext64.so -Updating mobile-deploy/libext.so -Updating mobile-deploy/libevent-2.1.so -Please restart hooker -``` -*** - - -# 应用目录通杀脚本 - -### url.js - -该脚本会 hook 应用中 构造 URL 或 URI 对象的多个关键方法,用于打印或分析网络请求相关的信息(如目标 URL) - -Hook 的目标方法 -java.net.URI(String) 构造函数 - -java.net.URL(String) 构造函数 - -okhttp3.Request.Builder.build() 方法(常用于创建 HTTP 请求) - -com.android.okhttp.Request.Builder.build()(系统自带 okhttp) - -android.net.Uri.parse(String) 方法(处理 URI 的常用工具方法) - -推荐命令:frida url.js - -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/url.jpg) -*** - -### just_trust_me.js -frida版本的just_trust_me,用于绕过 Android 系统中的证书校验逻辑,允许抓取 HTTPS 流量,适用于基于 OkHttp、HttpURLConnection 等网络库的应用。 - -支持boringssl unpinning,理论上支持全网所有app,除非像美团一样做了登录风控抓不了。 - -在hooker命令行模式封装了快捷命令justtrustme -这在上文 [执行justtrustme kill掉所有ssl验证](#11-执行justtrustme-kill掉所有ssl验证包括boringgssl)有介绍 - -亦可直接执行脚本 spawn just_trust_me.js -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/just_trust_me.gif) -*** - -### activity_events.js -当你需要跟踪start某个Activity启动时可执行,获取startActivity的intent信息和调用堆栈。 - -推荐命令:frida activity_events.js - -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/activity_events.gif) -*** - -### click.js -用于监听 Android 应用中点击事件(`OnClickListener`),主要用于分析用户交互操作,获取被点击View的真实ViewClass - -推荐命令:frida click.js - - -*** - -### android_ui.js -封装一些操作原生Android UI的函数。如startActivity(activityName)、home()、back()、finishCurrentActivity()、clickByText(text) 等等,命令使用得用attach './attach android_ui.js' 原理是借助radar.dex作为代理操作Android原生View。 - -推荐命令:frida android_ui.js - -*** - -### keystore_dump.js -在https双向认证的情况下,dump客户端证书为p12。存储位置:/data/user/0/{packagename}/client_keystore_{nowtime}.p12 证书密码: hooker。原理是hook java.security.KeyStore的getPrivateKey和getCertificate方法,因为客户端向服务发送证书必调这个方法。强烈建议keystore_dump.js用spawn模式启动, - -推荐命令:spawn keystore_dump.js +----------------------------------------------------------------------- +原类名:okhttp3.CertificatePinner +混淆类名:okhttp3.g -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_01.png) -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_02.png) -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_03.png) -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_04.png) -*** - -### edit_text.js - -用于跟踪获取 EditText 的 `getText()` 事件,并获取其真实 Class 类型。 -EditText 通常绑定搜索按钮或输入事件,是定位“搜索”接口实现代码的有效入口,辅助识别核心业务逻辑。 - -推荐命令:frida edit_text.js - -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/edit_text.png) -*** - -### hook_register_natives.js -用于拦截 Android JNI RegisterNatives 函数的 Frida 脚本,主要用于分析和修改原生方法(native methods)的注册过程 - -推荐命令:spawn hook_register_natives.js - -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/hook_RN.gif) -*** - -### text_view.js - -用于跟踪 TextView 的 `setText()` 和 `getText()` 调用,并输出其真实 Class。 -常用于提取页面展示的明文数据,以及获取堆栈调用信息,从而追踪业务层的 model 构造逻辑和数据源。 -推荐命令:spawn/attach text_view.js -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/text_view.png) +混淆方法0: +原方法签名:public void okhttp3.CertificatePinner.check(java.lang.String,java.util.List) +混淆方法签名:public void okhttp3.g.a(java.lang.String,java.util.List) throws javax.net.ssl.SSLPeerUnverifiedException ---- +----------------------------------------------------------------------- +原类名:okhttp3.OkHttpClient$Builder +混淆类名:okhttp3.OkHttpClient$Builder -### activity_events.js -用于跟踪 Android Activity 生命周期(如 `onCreate`、`onResume`)的 Frida 脚本,帮助分析 Activity 初始化逻辑。 +自动定位混淆方法失败,请去jadx打开okhttp3.OkHttpClient$Builder手动分析混淆方法 +----------------------------------------------------------------------- +原类名:okhttp3.internal.tls.OkHostnameVerifier +混淆类名:okhttp3.internal.i.d -推荐命令:spawn/attach activity_events.js ---- +混淆方法0: +原方法签名:public boolean okhttp3.internal.tls.OkHostnameVerifier.verify(java.lang.String,javax.net.ssl.SSLSession) +混淆方法签名:public boolean okhttp3.internal.i.d.verify(java.lang.String,javax.net.ssl.SSLSession) -### ssl_log.js +混淆方法1: +原方法签名:public boolean okhttp3.internal.tls.OkHostnameVerifier.verify(java.lang.String,java.security.cert.X509Certificate) +混淆方法签名:public boolean okhttp3.internal.i.d.a(java.lang.String,java.security.cert.X509Certificate) -用于在 native 层跟踪 SSL 握手过程并记录 `CLIENT_RANDOM`,配合 tcpdump 抓包数据后可用于 TLS 明文还原分析。 - -推荐命令:spawn/attach ssl_log.js - ---- - -### just_trust_me_for_ios.js - -iOS 版的证书校验绕过脚本,配合抓包代理使用,适用于 SSL Pinning 场景。 - -推荐命令:spawn/attach just_trust_me_for_ios.js - ---- - -### dump_dex.js - -执行 spawn dump_dex.js 可直接脱壳,适用于多数简单壳场景。 -Android ART 使用 dex2oat 编译 DEX 为 native 指令,有些脱壳失败时建议手动清除 `/data/app/-*/oat/arm64/`。 - -推荐命令:spawn dump_dex.js - ---- - -### trace_init_proc.js +----------------------------------------------------------------------- +``` +根据上面just_trust_me_okhttp_hook_finder.js跑的结果 +把okhttp3的hook点改成混淆的类: -用于 trace `init_proc` 函数调用流程的脚本。 -需手动指定 `startAddr`、`endAddr` 和模块名 `somodule`,适合分析 native 启动流程。 +![](assets/okhttp_justhook.png) +这个提交记录:https://github.com/CreditTone/hooker/commit/f47d2068320a58306735a623f12bd955cbd20632 -推荐命令:spawn/attach trace_init_proc.js -![trace_init_proc.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/trace_init_proc.png) ---- +# hooker调试命令行 -### hook_artmethod_register.js +### a - 打印Activity栈 +打印Activity栈的所有实例,当前界面排最前面。你可以立刻获取当前手机界面的Activity实现类、继承关系、实现接口、Activity中的所有属性值和方法声明。配合jadx动静分析效果最佳,分析Activity对象的内部情况将极快的提供逆向的线索。值得注意的是Activity中每个成员变量hooker会分配一个ObjectId,这是为了让你用c命令对内部成员变量进行扫描的。 +![](assets/a.gif) -用于拦截 Android ART 虚拟机中 `ArtMethod` 的注册函数,适合深入分析虚拟机行为与 native 方法绑定。 -推荐命令:spawn/attach hook_artmethod_register.js +### b - 打印Service栈 +打印Service栈的所有实例。和a命令一样,获取当前手机界面的Service实现类、继承关系、实现接口、Service中的所有属性值和方法声明。配合jadx动静分析效果最佳,分析Service对象的内部情况将极快的提供逆向的线索。值得注意的是Service中每个成员变量hooker会分配一个ObjectId,这是为了让你用c命令对内部成员变量进行扫描的。 +![](assets/b.gif) ---- +### c - 扫描指定Object +扫描指定ObjectId的对象,a、b命令扫描Activity和Service带出一托线索。结合jadx观察之成员变量的类型和值情况,你一定可以发现一些新的对象,想窥视之。那么请用c命令扫描吧,而c命令扫描完之后又会带出新的你感兴趣的线索(对象)。顺藤摸瓜,你可以找到很多有趣东西。 +![](assets/c.gif) -### find_anit_frida_so.js +### d - 展开Object[]、List或Map +展开一个Object[]、List或Map,并以Index/key->value的形式打印出来,从而进一步获取集合对象内部的每个对象类型和ObjectId。比如我们通过a命令查看某Activity发现属性o是一个Fragment数组'name:o static:false fromExtends:false type:[Landroidx.fragment.app.Fragment; objectId:tGErGHXLso value:[Landroidx.fragment.app.Fragment;@94023208',那么这个数组里面每个成员是什么我们就可以用d命令对其进行展开了。 +![](assets/d.png) -用于发现 app 中加载的可疑 anti-frida 动态库,识别顺序为:**谁最后加载、谁让 app 崩溃,谁就是反调试的嫌疑人**。 +### v - 以View的方式查看对象 +以View的方式查看一个对象,除了有c命令全部的功能之外。v命令会先强转对象为View,然后获取view绑定的OnClickListener、OnLongClickListener、OnTouchListener、OnFocusChangeListener、OnEditorActionListener、OnItemClickListener等等,这些对象也将完全探测出来。比如对于属性:'name:h static:false fromExtends:false type:com.ttpc.bidding_hall.weight.HomeTabButton viewId:2131297762 objectId:GKuWPZOyY0 value:com.ttpc.bidding_hall.weight.HomeTabButton@227103246' 如下图用v命令查看我们可以发现HomeTabButton绑定了一个点击事件com.ttpai.track.g,这样的话你去jadx找到这个类就知道处理这个按钮的逻辑啦!!!此外,v 命令还可以跟上ViewId,但是ViewId对于每个View不一定都存在并且也不一定唯一,这个具体细节可以了解Android应用开发。 +![](assets/v.png) -推荐命令:spawn/attach find_anit_frida_so.js -![find_anti_frida_so.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/find_anti_frida_so.png) +### e - 检测类在内存中是否存在 +检测一个类在内存中是否存在。大部分情况下静态分析的类在内存中会存在,但是有时app会做热更新可能会出现类被替换的情况。作为一个严谨的逆向工程师在对类进行操作之前检测类是否存在内存中是个好习惯。如:e com.bytedance.frameworks.encryptor.EncryptorUtil 输出:True表示存在 False表示不存在 +![](assets/exists_class.gif) ---- +### s - 正则表达式扫描类 +用正则表达式在内存中扫描类。比如你对某些关键词类感兴趣,完全可以使用s进行扫描。配合jadx查看类实现代码爽歪歪!当然jadx自带类搜索功能但是只是静态的,并且jadx搜索功能是吃内存的,没有32G内存使用起来好卡。这时候s命令或许你是不错的选择。 +![](assets/s.png) -### jni_method_trace.js +### j - 生成指定类的hook脚本 +生成指定类名称的hook脚本,也是hooker最核心的功能之一。相比objection,hooker生成的脚本有标注生产脚本的apk版本和类名。并且每个方法内部已经具备打印堆栈的功能,包括调用时间、线程id、线程名、调用对象的hashcode,调用方法用时都有很详细的描述。 +![](assets/j0.png) +![](assets/j1.png) +![](assets/j2.png) +此脚本参考[RequestEncryptUtils.js](com.ss.android.ugc.aweme/com.bytedance.frameworks.core.encrypt.RequestEncryptUtils.js "RequestEncryptUtils.js") -用于追踪 Native 层回调 Java 方法的行为,可观察 so 层与 Java 的交互,帮助分析 JNI 层调用栈。 +hooker生成脚本和objection生成脚本优势对比 -推荐命令:spawn/attach jni_method_trace.js +| 项目 | 生成脚本命令 | 能否打印堆栈 | 包含辅助方法 | 脚本版本信息注释 | 脚本可定制性 | +| -------- | -----: | :----: | :----: | :----: | :----: | +| hooker | j [class_name] -o [outputpath].js | 能 | 包含 | 带apk version和生成命令 | 好 | +| objection | android hooking generate simple [class_name] | 不能(白纸一张) | 不包含 | 不带 | 一般 | ---- +### k - 生成字符串hook脚本 +使用k 1821053,那么应用工作目录下会生成str_1821053.js。只要字符串产生并包含1821053这个关键词,就会打印产生地方的堆栈信息。原理基于java字符串拼接代码,编译后变成StringBuilder做append -### replace_dlsym_get_pthread_create.js +```java +String url = "http://www.example.com/login?"+ "sign=" + getSign(); +``` -专门对抗 `libmsaoaidsec.so` 中使用 `dlsym` 获取 `pthread_create` 的反调试方式。 -本脚本 hook `dlsym`,用于劫持线程创建行为,从而对抗动态加载的反调试代码。 +以上代码编译后会生成如下 -推荐命令:spawn/attach replace_dlsym_get_pthread_create.js -![replace_pthread_create.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/replace_pthread_create.png) +```java +String url = new StringBuilder("http://www.example.com/login?").append("sign=").append(getSign()).toString(); +``` ---- +不过这个思路有点儿类似暴力破解,而且非常影响app进程的运行速度,崩溃也是可能的。我的认为只要能搞出一些线索,崩溃也是值得的。崩溃我重启就是了。你觉得呢? -### find_boringssl_custom_verify_func.js -用于查找 `boringssl` 中注册的自定义证书验证函数,通过 hook `SSL_CTX_set_custom_verify` 定位目标函数,并实现强制信任(返回 0)。 -**使用建议:** -执行前请清除目标 app(如某音)的缓存,以保证函数重新注册。 +hooker高级应用 +================= -推荐命令:spawn find_boringssl_custom_verify_func.js -![find_boringssl_custom_verify.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/find_boringssl_custom_verify.png) -![hook_verify.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/hook_verify.png) -![mouyin_capture_33.9.0.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/mouyin_capture_33.9.0.png) +## radar.dex +在hooker根目录中有一个radar.dex,这是为操作java类的增强库。 ---- +## 脚本的内置函数 +脚本是frida的核心,一个脚本不能只做打印堆栈信息的事情。我们还要进一步深挖脚本的潜在价值。比如我们改变被hook对象的内部成员变量的值、直接调用对象的方法、patch我们的dex。所以在hooker生成的每个脚本当中,我还内置了你可能需要用到的方法用于定制脚本。下面我就开始介绍这些方法的作用吧。 -### get_device_info.js +### 1. loadDexfile(dexfile) +加载一个dex文件到app进程中。dexfile是dex在手机上绝对路径,调用此方法必须保证dex文件用户和组权限对app进程的可见性。 -用于全面获取设备指纹信息,包括: +### 2. checkLoadDex(className, dexfile) +先检测className是否存在内存中,如果不存在加载一个dex文件到app进程中。dexfile是dex在手机上绝对路径,调用此方法必须保证dex文件用户和组权限对app进程的可见性。 -- Android ID、IMEI、指纹、厂商、系统信息 -- 安装的所有应用(含系统 app) -- 传感器信息(如名称、厂商、延迟) -- 系统状态(是否 root、运行时、内核信息等) +### 3. loadXinitDexfile(dexfile) +加载一个dex文件到app进程中。与loadDexfile、checkLoadDex不同,此方法直接从/data/user/0/{packageName}/xinit/路径下找dex文件。比如我们要加载上面基于[xinitdeploy](#4-xinitdeploy)命令部署的patch.dex,直接loadXinitDexfile("patch.dex");就可以给app进程注入dex了。到了这里你明白[xinitdeploy](#4-xinitdeploy)的良苦用心了没有! -推荐命令:attach get_device_info.js -![](https://raw.githubusercontent.com/CreditTone/img_resources/main/get_device_info_attach.png) +### 4. loadXRadarDexfile() +加载radar.dex文件到app进程中。radar.dex内部包含操作java对象的增强功能,如果你需要使用fastTojson、getPrettyString、storeObjectAndLog等方法,必需在脚本加载时调用一次loadXRadarDexfile()。 -提供以下 4 个调用方法: +### 5. fastTojson(javaObject) +基于radar.dex中fastjson序列化一个java对象为json字符串。该方法依赖radar.dex,使用前必须loadXRadarDexfile()。注意loadXRadarDexfile()进行一次即可,无需多次调用。 -- `getBasicInfo()`:基础信息 -- `getInstalledPackages()`:应用列表 -- `getSensos()`:传感器信息 -- `getSystemInfo()`:系统与运行环境 - ![](https://raw.githubusercontent.com/CreditTone/img_resources/main/get_device_info_functions.png) +### 6. getPrettyString(javaObject) +类似与toString(),与toString()不同的是它将判断子类是否实现toString方法,如果没有现实toString或者类是android.os.Parcelable可能会引起循环调用的异常,所以getPrettyString会根据情况采用[className]@[hashCode]的方法来规避。该方法依赖radar.dex,使用前必须loadXRadarDexfile()。注意loadXRadarDexfile()进行一次即可,无需多次调用。 ---- +### 7. getField(javaObject, fieldName) +获取对象的属性,这是来弥补frida语法中this.a.b.c.d深度查找的不足。该方法依赖radar.dex,使用前必须loadXRadarDexfile()。注意loadXRadarDexfile()进行一次即可,无需多次调用。 -### apk_shell_scanner.js +在frida中深度查找是不支持的,以下方式获取对象将发生错误。 +```js +let d = this.a.b.c.d; +``` +所以你可以使用getField(javaObject, fieldName)来替代以上的逻辑 +```js +let aObj = getField(this, "a"); +let bObj = getField(aObj, "b"); +let cObj = getField(bObj, "c"); +let dObj = getField(cObj, "d"); +``` -动态识别 APK 加壳技术的脚本,支持多种主流壳,如娜迦、爱加密、360、梆梆、腾讯御、网易易盾等。 +### 8. storeObjectAndLog(javaObject) +将对象存储至对象缓存中,同时输出对象缓存id。然后你可以用c [objectId],扫描对象,这将帮助你更好的窥视内存。该方法依赖radar.dex,使用前必须loadXRadarDexfile()。注意loadXRadarDexfile()进行一次即可,无需多次调用。 -推荐命令:attach apk_shell_scanner.js +## 原生ui自动化 +hooker借助radar.dex中gz.radar.AndroidUI的实现,将代码直接打到app进程当中实现直接操作View对象。这种效率已经超越了所有的三方自动化框架,因为无论哪种自动化框架都是基于C/S架构的,而hooker通过打补丁的方式是直接侵入式的修改app内部的代码逻辑。 -识别结果示例: -- This app is protected by {爱加密} -- This app is not protected or uses an unknown protection scheme +不过android_ui.js只是一个ui简单操作的体验工具,更高效的ui操作还是要结合每个应用实际情况定制patch代码。 ---- +应用工作目录输入后./attach android_ui.js进入ui操作命令行,你可以操作以下几个方法 -### bypass_frida_svc_detect.js +### 1. startActivity(activityName) +不传intent和action,强制启动一个Activity。比如,2020年4月份某信刚上视频号功能的时候,我的微信小号没有体验资格——也就是没有"视频号"那个按钮,不给我按钮我就启动不了了吗?按钮背后的逻辑就是执行了一个startActivity的操作,所以我就实现了这个方法————强制启动v信视频号的搜索界面。 -绕过 app 对 Frida Server 的检测逻辑,适用于反调试保护较强的目标应用。 +```js +startActivity("com.tencent.mm.plugin.finder.search.FinderFeedSearchUI") +``` -推荐命令:spawn/attach bypass_frida_svc_detect.js +不过startActivity不一定成功,因为应用很多Activity都需要特定的intent和action,这个在应用的AndroidManifest.xml中有定义,另外你还有结合[activity_events.js](#2-activity_eventsjs)去参照原来的启动代码。比如我要强制打开一个用户资料的Activity,那你必须传一个userid或者uid什么数据给他,而且你还得知道它定义的数据方式。这时候你需要定位原来启动Activity代码,这就是[activity_events.js](#2-activity_eventsjs)的意义所在。比如,下面经过我的分析启动某一视频号作者主页面的代码应该这样实现。 ---- +```java +Intent intent = new Intent(); +intent.putExtra("finder_username", username); +com.tencent.mm.plugin.finder.g.a aVar = com.tencent.mm.plugin.finder.g.a.pPL; +com.tencent.mm.plugin.finder.g.a.enterFinderProfileUI(Android.getTopActivity(), intent); +``` -### bypass_root_detect.js +### 2. contextStartActivity(activityName) +contextStartActivity与startActivity不同,它会先获取应用的context。然后调用context的startActivity方法。 -用于绕过 root 检测逻辑,使得 root 环境下也能正常运行 app。 +### 3. topActivityStartActivity(activityName) +topActivityStartActivity先获取栈顶的Activity实例,然后调用实例的startActivity方法。startActivity就是topActivityStartActivity。 -推荐命令:spawn/attach bypass_root_detect.js +### 4. home() +模拟点击home按钮 ---- +### 5. back() +模拟点击back按钮 -### bypass_vpn_detect.js +### 6. finishCurrentActivity() +退出栈顶Activity。原理是先获取栈顶的Activity实例,然后调用其finish()方法来主动结束。 -用于绕过 VPN 检测逻辑,避免 app 阻止使用代理、VPN 或抓包工具。 +### 7. clickByText(text) +点击包含text文本的按钮。如果界面上存在多个包含text的按钮会优先选择找到的第一个。 -推荐命令:spawn/attach bypass_vpn_detect.js +### 8. clickById(id) +点击指定view id的按钮。你可以用uiautomatorviewer查看viewid,也可以用viewTree()方法输出view的json树来找到你要的元素。uiautomatorviewer在你{AndroidSdk}/tools/bin/路径下。 ---- +### 9. hover(x,y,upStepLength) +滑动屏幕。x,y传数字表示按下坐标,upStepLength的绝对值是步长表示滑动的长度。当upStepLength为正数的时候表示要向上滑动,当upStepLength为负数的时候表示向下滑动。 -### hook_encryption_algo.js +### 10. viewTree() +获取json格式的view树 -用于 hook 典型加密算法(如 AES、RSA、HMAC 等)的实现函数,分析加密参数、明文与密文数据。 +![](assets/android_ui_view_tree2.gif) +![](assets/android_ui_view_tree.png) +此ViewTree结果参考[viewTree.json](com.ss.android.ugc.aweme/viewTree.json "viewTree.json") -推荐命令:spawn/attach hook_encryption_algo.js ---- -### hook_encryption_algo2.js -拓展版本的加密算法 hook 脚本,适用于更复杂的加密场景或多路加密调用链。 +## 远程frida支持 +在hooker根目录有一个.hooker_driver文件,内容默认是-U表示通过usb连接frida-server。 +```shell +stephen@ubuntu:~/hooker$ cat .hooker_driver +-U +``` +如果你的frida-server绑定在0.0.0.0:27042,并且你需要远程连接的话。你需要知晓你的手机局域网ip比如是192.168.0.105,则把.hooker_driver改为如下内容即可实现hooker全局远程调试。 +```shell +stephen@ubuntu:~/hooker$ cat .hooker_driver +-H 192.168.0.105:27042 +``` -推荐命令:spawn/attach hook_encryption_algo2.js -### webview_enable_debug.js +# 更新教程 +hooker没有版本的概念,更新可以采用两种方式。建议每周一更,更新步骤建议采用如下操作顺序 -该脚本用于 Hook Android 应用中的 WebView 行为,特别是在 WebView 初始化和执行 JavaScript 时进行监控和调试。适用于分析 WebView 加载的页面、动态注入的 JavaScript 代码,以及定位 JS 调用位置,常用于逆向分析、抓取数据或识别加固方案中的 Web 容器。 +### 1. clone最新项目 +```shell +git clone https://github.com/CreditTone/hooker.git +``` +如果git clone比较慢,你可以下载文件解压https://github.com/CreditTone/hooker/archive/refs/heads/master.zip -推荐命令:spawn webview_enable_debug.js +### 2. 方式一覆盖核心文件到你的hooker +此种方式适用于你有自己的hooker本地仓库,所以只覆盖最新hooker的核心功能文件是便于你管理自己的hooker的。 +执行以下命令将核心文件覆盖到你本地的hooker - +```shell +export LATEST_HOOKER=~/Download/hooker #这里要设置你下载的最新hooker目录 +export MY_HOOKER=~/hooker #这里要设置你的hooker目录 + +#上面两个环境变量改好之后,下面的命令可以照抄 +cp -f $LATEST_HOOKER/hooker $MY_HOOKER/hooker +cp -f $LATEST_HOOKER/hooker.py $MY_HOOKER/hooker.py +cp -f $LATEST_HOOKER/radar.dex $MY_HOOKER/radar.dex +cp -f $LATEST_HOOKER/run_env.py $MY_HOOKER/run_env.py +cp -f $LATEST_HOOKER/xinitdeploy.py $MY_HOOKER/xinitdeploy.py +cp -f $LATEST_HOOKER/spider.py $MY_HOOKER/spider.py +cp -f $LATEST_HOOKER/colorful.py $MY_HOOKER/colorful.py +cp -f $LATEST_HOOKER/requirements.txt $MY_HOOKER/requirements.txt +cp -rf $LATEST_HOOKER/js/* $MY_HOOKER/js/ +cp -rf $LATEST_HOOKER/mobile-deploy/* $MY_HOOKER/mobile-deploy/ +``` +### 3. 方式二覆盖你的应用工作目录到最新hooker +如果你不需要维护自己的hooker版本,就可以用这个方式。简单粗暴! -# Windows安装WSL +```shell +export LATEST_HOOKER=~/Download/hooker #这里要设置你下载的最新hooker目录 +export MY_HOOKER=~/hooker #这里要设置你的hooker目录 -WSL是适用于Linux 的Windows 子系统(WSL)允许开发人员直接在Windows 上运行GNU/Linux 环境(包括大多数命令行工具、实用工具和应用程序),无需传统虚拟机或双启动设置的开销。 +#你有多少个目录就拷贝多少个吧 +cp -rf $MY_HOOKER/{your_app_package} $LATEST_HOOKER/ +``` -### 1. 安装wsl ubuntn24.04 -访问:https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#downloading-distributions +hooker实战应用 +================= -下载Ubuntu24.04 +### 亲测好用的脱壳工具 +https://github.com/CodingGay/BlackDex -双击Ubuntu2404-240425.AppxBundle 安装Ubuntu +https://github.com/GuoQiang1993/Frida-Apk-Unpack +https://github.com/hanbinglengyue/FART -### 2. 进入wsl,配置代理 -窗口输入wsl进入ubuntu命令行 +### 通信降级案例(亲测有效) +https://blog.csdn.net/qq314000558/article/details/105958847 -- cmd -- wsl +# 关于作者 -切换到root用户 +```javascript +var author = { + name : "stephen", + bilibili : "https://space.bilibili.com/105035514", + email : "1273568669@qq.com", + experience : ["Android应用开发", "网络爬虫", "Android逆向", "JAVA/Go后台开发", "中间件开发"] +} +``` -- sudo su +# 加密货币打赏 -配置翻墙代理,如果你有 +### Dogecoin tipping -- export http_proxy="http://10.115.164.50:8080" -- export https_proxy="http://10.115.164.50:8080" +![](assets/dogecoin.jpeg) -### 3. 安装python3.8和frida -- apt update -- apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev -libreadline-dev libsqlite3-dev wget curl llvm xz-utils tk-dev -libffi-dev liblzma-dev -- apt install -y git -- apt install -y pyenv -- pyenv install 3.8.8 -- pyenv local 3.8 +### Kevacoin tipping -# 自定义frida-server -- 将您自定义的frida-server文件拷贝到mobile-deploy文件夹下 -- 修改hooker.py,default_frida_server_arm和default_frida_server_arm64变量的名字为你自定义的文件名 +![](assets/kva.jpeg) -```python -default_frida_server_arm = "your-custom-frida-server-android-arm" -default_frida_server_arm64 = "your-custom-frida-server-android-arm64" -``` -# hooker命令行快捷键 +没有Kevacoin的同学可以用你的普通pc进行挖矿。这个币目前全网算力不高,再垃圾的cpu都能挖的动。[挖矿教程https://www.dxpool.com/help/zh/kva-mining-tutorial](https://www.dxpool.com/help/zh/kva-mining-tutorial),教程中获取钱包地址部分可以跳过,直接填入我的钱包地址VLuXBaaDzGRukEFPwRmzKm4zhYvX4Xmy1R进行挖矿。只要跑半个小时我就有收益,感谢支持!!! -- Ctrl + U:整行清空 -- Ctrl + W:删除一个单词 -- Ctrl + K:从光标删到行尾 +### End +[1]: https://github.com/frida/frida "frida" diff --git a/README_EN.md b/README_EN.md deleted file mode 100644 index 396aaad..0000000 --- a/README_EN.md +++ /dev/null @@ -1,642 +0,0 @@ -⚠️ Disclaimer -All contents of this project are intended solely for learning and technical exchange purposes. The goal is to help developers understand the structure and internal mechanisms of mobile applications. - -This project does not contain any cracking operations or infringing content targeting specific applications. -Some simple unpacking techniques related to certain apps are widely available on major technical forums and do not cause actual harm to the applications. -This project is not intended to assist with any illegal activities, including but not limited to bypassing copyright protection, modifying app functionality, or accessing unauthorized data. -Please ensure compliance with relevant laws and regulations when using the tools provided by this project, and use them only for personal learning or research purposes. - -

English | 简体中文

- -# 👋 Welcome to Hooker Reverse Engineering Toolkit -![GitHub stars](https://img.shields.io/github/stars/CreditTone/hooker?style=flat-square) -![GitHub forks](https://img.shields.io/github/forks/CreditTone/hooker?style=flat-square) -![GitHub code size](https://img.shields.io/github/languages/code-size/CreditTone/hooker?style=flat-square) -![Python](https://img.shields.io/badge/python-3.8.8-blue?style=flat-square) -![frida](https://img.shields.io/badge/frida-16.7.19-blue?style=flat-square) - -Hooker is a reverse engineering toolkit based on Frida, designed to provide Android reverse engineers with a comfortable command-line interface, - -A unified script package management system - -Universal (通杀) scripts - -Automated hook script generation - -In-memory roaming for detecting Activity and Service components - -A Frida-based implementation of JustTrustMe - -Global app support for boringssl unpinning - -One picture to prove why you need Hooker: -![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg) - -- 1. A Frida-based implementation of JustTrustMe for universal SSL pinning bypass, with ongoing maintenance. -- 2. An embedded webserver that can quickly expose in-app capabilities as HTTP endpoints for automation and API-style workflows. -- 3. Automated Frida hook script generation with detailed, extensible templates. -- 4. One-click SOCKS5 proxy setup without requiring third-party apps such as SocksDroid. -- 5. A highly streamlined command-line workflow that makes daily reversing much more comfortable. - -## 📚 Table of Contents - -- [Quick Start](#quick-start) - - [1. Clone the repository](#1-clone-the-repository) - - [2. Install Python dependencies](#2-install-python-dependencies) - - [3. Connect your rooted device via USB](#3-connect-your-rooted-device-via-usb) - - [4. Launch Hooker](#4-launch-hooker) - - [5. Enter the package name of the target app](#5-enter-the-package-name-of-the-target-app) - - [6. View help information](#6-view-help-information) - - [7. Embedded webserver](#7-embedded-webserver) - - [8. Generate Frida hook scripts for a specific class and method](#8-generate-frida-hook-scripts-for-a-specific-class-and-method) - - [9. List all available Frida scripts](#9-list-all-available-frida-scripts) - - [10. Attach and execute a specific Frida script](#10-attach-and-execute-a-specific-frida-script) - - [11. Set a SOCKS5 proxy for the app](#11-set-a-socks5-proxy-for-the-app) - - [12. Run JustTrustMe to disable all SSL pinning (including boringssl)](#12-run-justtrustme-to-disable-all-ssl-pinning-including-boringssl) - - [13. Spawn the app and execute a specific Frida script](#13-spawn-the-app-and-execute-a-specific-frida-script) - - [14. Clear the proxy settings](#14-clear-the-proxy-settings) - - [15. Restart the app](#15-restart-the-app) - - [16. Get the app's UID and PID](#16-get-uid-and-pid) - - -# 🚀 Quick Start - -Ensure your device is rooted. No need to manually start frida-server or do any configuration—Hooker handles everything for you. - - -### 1. Clone the repository -```shell -stephen@ubuntu:~$ git clone https://github.com/CreditTone/hooker.git -stephen@ubuntu:~$ cd hooker -``` - -### 2. Install Python dependencies -```shell -stephen@ubuntu:~/hooker$ pip3 install -r requirements.txt -``` - - -### 3. Connect your rooted device via USB -```shell -stephen@ubuntu:~/hooker$ adb devices -List of devices attached -FA77C0301476 device -``` - - -### 4. Launch Hooker -```shell -stephen@ubuntu:~/hooker$ python3 hooker.py -hooker Let's enjoy reverse engineering together ------------------------------------------------------------------------------------------------ -PID APP IDENTIFIER EXIST_REVERSE_DIRECTORY -0 全球上网 com.miui.virtualsim ❌ -0 爱奇艺 com.qiyi.video ❌ -0 红手指云手机 com.redfinger.app ❌ -0 Reqable com.reqable.android ❌ -0 美团 com.sankuai.meituan ✅ -0 得物 com.shizhuang.duapp ❌ -0 某皮 cxm.shxpxx.sg ✅ -0 微博 com.sina.weibo ❌ -0 今日头条 com.ss.android.article.news ✅ -0 西瓜视频 com.ss.android.article.video ✅ -0 懂车帝 com.ss.android.auto ✅ -0 抖音火山版 com.ss.android.ugc.live ✅ -0 抖音精选 com.ss.android.yumme.video ❌ -0 淘宝 com.taobao.taobao ✅ -0 腾讯视频 com.tencent.qqlive ❌ -0 Termux com.termux ❌ -0 轻奢 com.tm.bachelorparty ✅ -0 WiFi ADB com.ttxapps.wifiadb ❌ -0 VMOS Pro com.vmos.pro ✅ -0 游戏中心 com.xiaomi.gamecenter ❌ -0 小米商城 com.xiaomi.shop ❌ -0 米家 com.xiaomi.smarthome ❌ -0 小米有品 com.xiaomi.youpin ✅ -0 小红书 com.xingin.xhs ✅ -0 运满满货主 com.xiwei.logistics.consignor ✅ -0 拼多多 com.xunmeng.pinduoduo ✅ -0 EnvCheck com.yimian.envcheck ✅ -0 check_env com.yuuki.check_env ❌ -0 TikTok com.zhiliaoapp.musically ❌ -0 XPrivacyLua eu.faircode.xlua ❌ -0 imToken im.token.app ❌ -0 SocksDroid net.typeblog.socks ❌ -0 F-Droid org.fdroid.fdroid ❌ -0 ProxyDroid org.proxydroid ❌ -3457 手机管家 com.miui.securitycenter ✅ -3509 优信拍 com.uxin.buyerphone ✅ -18780 抖音 com.ss.android.ugc.aweme ✅ -20174 应用商店 com.xiaomi.market ❌ -20913 设置 com.android.settings ❌ -30500 小爱同学 com.miui.voiceassist ❌ -32163 相机 com.android.camera ✅ -Please enter the identifier that needs to be reversed -hooker(Identifier): -``` -*** - -### 5. Enter the package name of the target app -```shell -hooker(Identifier): cxm.shxpxx.sg -✅ App cxm.shxpxx.sg is already in the foreground -Creating working directory: cxm.shxpxx.sg -Generating frida shortcut command... -Generating built-in frida script... -pull /data/app/cxm.shxpxx.sg-L8zkrpFVICv0-hOrtmPPxA==/base.apk to cxm.shxpxx.sg/ShopeeSG_3.43.40.apk successful -Working directory create successful -just_trust_me.js empty.js keystore_dump.js -edit_text.js activity_events.js find_boringssl_custom_verify_func.js -ssl_log.js hook_register_natives.js click.js -get_device_info.js apk_shell_scanner.js dump_dex.js -object_store.js hook_artmethod_register.js replace_dlsym_get_pthread_create.js -just_trust_me_for_ios.js trace_initproc.js android_ui.js -jni_method_trace.js url.js just_trust_me_okhttp_hook_finder_for_android.js -text_view.js find_anit_frida_so.js -某皮 > -``` -![hooker_enter_debug.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_enter_debug.gif) -*** - - - - -### 6. View help information - -```shell -某皮 > help -h, help show this help message -a, activitys show the activity stack -s, services show the service stack -o, object [object_id] show object info by object_id -v, view [view_id] show view info by view_id of view -gs, generatescript [class_name:method_name] specify the class name and method name to generate a frida hook java script file. For example: generatescript - okhttp3.Request$Builder:addHeader -p, proxy [socks5_proxy_server] set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998 -up, unproxy remove socks5 proxy for this app -trust, justtrustme quickly spawn just_trust_me.js script to kill all ssl pinning -ls list all the frida scripts of the current app -attach [script_file_name] quickly execute a frida script, similar to executing the command "frida -U com.example.app -l xxx.js". For example: attach url.js -spawn [script_file_name] quickly spawn a frida script, similar to executing the command "frida -U -f -n com.example.app -l xxx.js". For example: spawn - just_trust_me.js -restart restart this app -pid get pid of this app main process -uid get pid of this app -exit return to the previous level -某皮 > -``` -![hooker_help.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_help.gif) -*** - - -### 7. Embedded webserver - -Hooker can inject a lightweight webserver into the target app process. Once started, it launches an HTTP service inside the app. The default port is `8080`. This service can expose both your custom patch controllers and a built-in set of debugging endpoints. - -- Start the built-in webserver - -```shell -某音火山版 > webserver start -Http server port: 8080 -Http server: http://10.112.101.249:8080 -``` - -After the built-in webserver starts, open the root page in a browser to see all registered APIs. Common built-in capabilities include: - -- Service management: `/` shows the welcome page and API list, and `/stop` stops the current webserver. -- UI automation: `/hooker/ui/...` provides view clicking, text-based clicking, coordinate tapping, setting EditText values, triggering Back/Home, launching activities, querying screen info, swiping pagers, scrolling RecyclerView, and dismissing dialogs. -- UI hierarchy export: `/hooker/uiauto/dump`, `/hooker/uiauto/window_dump.xml`, and `/hooker/uiauto/window_dump.json` export the current window hierarchy for inspection and control discovery. -- Screenshot capture: `/hooker/screencap/screenshot` uses the system `screencap` command, while `/hooker/mediaprojection/...` supports MediaProjection permission flow and full-screen PNG capture. -- App information: `/hooker/appinfo`, `/hooker/appinfo/shared_prefs`, `/hooker/appinfo/databases`, and `/hooker/appinfo/read_table` let you inspect package metadata, permissions, signatures, shared preferences, database schemas, and table rows. -- Class and object helpers: `/hooker/classhelper/invoke_static_method` and `/hooker/classhelper/invoke_method` let you invoke static methods or stored object methods over HTTP. -- File serving: `/file?filename=...` returns an absolute-path file or a file generated in the webserver cache directory. -- MCP-style UI tools: `/hooker/mcp/ui/tools` and `/hooker/mcp/ui/call` package common UI actions into a consistent tool interface for external scripts or agents. - -- Start a custom webserver - -```shell -某宝 > webserver start taxbax-patch.jar -Converting taxbax-patch.jar to taxbax-patch.dex... -Successfully converted to taxbax-patch.dex (7160 bytes) -push file OK /data/user/0/com.taxbax.taxbax/hooker_server.dex -Http server port: 2026 -Http server: http://10.112.101.249:2026 -``` - -Here `taxbax-patch.jar` is essentially a business plugin running inside the target app process. Hooker converts the jar to dex, injects it into the app, scans annotated classes, and registers them as HTTP routes. - -Custom webservers are useful for: - -- Exposing app-internal business capabilities such as search, comments, product details, signatures, encryption/decryption, user profiles, or live-stream APIs. -- Reusing the target app's own login state, networking stack, environment values, and in-memory objects instead of rebuilding protocol details externally. -- Wrapping asynchronous callbacks, observables, listeners, or page-object calls into synchronous HTTP endpoints that return JSON or plain text. - -Patch projects typically define endpoints like this: - -- Use `@HookerWebServerConfiguration(port = 2026)` to specify the port. If omitted, the default is `8080`. -- Use `@HookerController("/taobao")` or `@HookerController("/douyin")` to define the route prefix. -- Use `@HookerRequestMapping(path = "/getProductDetail")` to expose concrete endpoints. -- Use `@HookerRequestParam` and `@HookerRequestPostJson` to receive query parameters and POST JSON. - -This is also the recommended way to build mobile-facing APIs with Hooker: keep generic debugging features in the main repository, keep app-specific business logic in patch projects, and expose those capabilities through the embedded webserver. - -- Webserver persistence - -If you already have a patch project for an app and need to deploy it at scale, the temporary `frida + hooker` injection model can become heavy. For this scenario, the author provides an Xposed plugin named `HookerServer`: - -`https://github.com/CreditTone/HookerServer` - -If the device supports Xposed/LSPosed, you can use this plugin to persist the webserver inside the target app. - -Steps: - -1. Download the latest APK from `https://github.com/CreditTone/HookerServer/releases`. -2. Push `patch.dex` to `/data/user/0/{package}/hooker_server.dex`. -3. Enable `HookerServer` for the target app in Xposed/LSPosed. -4. Restart the app, and the webserver will start automatically. - -The `patch.dex` file becomes available after the first time you deploy `patch.jar`; Hooker automatically converts the jar to dex and places the output in the target app's working directory. -*** - - -### 8. Generate Frida hook scripts for a specific class and method - -![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg) - -- Command Syntax:gs, generatescript [class_name:method_name] - - -- 8.1 Generate a Frida hook script for a specific method: -gs okhttp3.Request$Builder:addHeader — the parameter part (String, String) is not required. - -```shell -某信拍 > gs okhttp3.Request$Builder:addHeader(String, String) -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder.addHeader.js -某信拍 > -``` - -```js -//cat okhttp3.Request.Builder.addHeader.js -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_method_addHeader_2grl = okhttp3_Request_Builder_clz.addHeader.overload('java.lang.String', 'java.lang.String'); - okhttp3_Request_Builder_clz_method_addHeader_2grl.implementation = function(string, string_x2) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.addHeader(java.lang.String,java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_addHeader_2grl.call(this, string, string_x2); - console.log("header name:" + string + " header value:" + string_x2); - printBeat(beat); - return ret; - }; -}); -``` -*** - - - -- 8.2 Generate a Frida hook script for all member methods of a specified class: -gs okhttp3.Request$Builder - -```shell -某信拍 > generatescript okhttp3.Request$Builder -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder.allfunc.js -``` -*** - -```js -//cat okhttp3.Request.Builder.allfunc.js -//okhttp3.Request$Builder -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_method_header_ng3n = okhttp3_Request_Builder_clz.header.overload('java.lang.String', 'java.lang.String'); - okhttp3_Request_Builder_clz_method_header_ng3n.implementation = function(string, string_x2) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.header(java.lang.String,java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_header_ng3n.call(this, string, string_x2); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_cacheControl_q8q5 = okhttp3_Request_Builder_clz.cacheControl.overload('okhttp3.CacheControl'); - okhttp3_Request_Builder_clz_method_cacheControl_q8q5.implementation = function(cacheControl) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.cacheControl(okhttp3.CacheControl)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_cacheControl_q8q5.call(this, cacheControl); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_method_bjk9 = okhttp3_Request_Builder_clz.method.overload('java.lang.String', 'okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_method_bjk9.implementation = function(string, requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.method(java.lang.String,okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_method_bjk9.call(this, string, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_head_a5nq = okhttp3_Request_Builder_clz.head.overload(); - okhttp3_Request_Builder_clz_method_head_a5nq.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.head()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_head_a5nq.call(this); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_headers_to5i = okhttp3_Request_Builder_clz.headers.overload('okhttp3.Headers'); - okhttp3_Request_Builder_clz_method_headers_to5i.implementation = function(headers) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.headers(okhttp3.Headers)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_headers_to5i.call(this, headers); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_post_heaq = okhttp3_Request_Builder_clz.post.overload('okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_post_heaq.implementation = function(requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.post(okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_post_heaq.call(this, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_build_rmqx = okhttp3_Request_Builder_clz.build.overload(); - okhttp3_Request_Builder_clz_method_build_rmqx.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request okhttp3.Request$Builder.build()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_build_rmqx.call(this); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_patch_hp9u = okhttp3_Request_Builder_clz.patch.overload('okhttp3.RequestBody'); - okhttp3_Request_Builder_clz_method_patch_hp9u.implementation = function(requestBody) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.patch(okhttp3.RequestBody)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_patch_hp9u.call(this, requestBody); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_url_0owi = okhttp3_Request_Builder_clz.url.overload('java.lang.String'); - okhttp3_Request_Builder_clz_method_url_0owi.implementation = function(string) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_url_0owi.call(this, string); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_removeHeader_uzb9 = okhttp3_Request_Builder_clz.removeHeader.overload('java.lang.String'); - okhttp3_Request_Builder_clz_method_removeHeader_uzb9.implementation = function(string) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.removeHeader(java.lang.String)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_removeHeader_uzb9.call(this, string); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_url_ykbd = okhttp3_Request_Builder_clz.url.overload('java.net.URL'); - okhttp3_Request_Builder_clz_method_url_ykbd.implementation = function(url) { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.net.URL)'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_url_ykbd.call(this, url); - printBeat(beat); - return ret; - }; - var okhttp3_Request_Builder_clz_method_delete_dqyl = okhttp3_Request_Builder_clz.delete.overload(); - okhttp3_Request_Builder_clz_method_delete_dqyl.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.delete()'; - var beat = newMethodBeat(beatText, executor); - var ret = okhttp3_Request_Builder_clz_method_delete_dqyl.call(this); - printBeat(beat); - return ret; - }; - //.......省略N行代码 -``` -*** - - - -- 8.3 Generate a Frida hook script for the constructor(s) of a specified class: -gs okhttp3.Request$Builder:_ or gs okhttp3.Request$Builder:\ - -```shell -某信拍 > gs okhttp3.Request$Builder:() -Generating frida script, please wait for a few seconds -frida hook script: okhttp3.Request.Builder._init.js -``` - -```js -//cat okhttp3.Request.Builder._init.js -//okhttp3.Request$Builder:() -Java.perform(function() { - var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder'); - var okhttp3_Request_Builder_clz_init_uw3i = okhttp3_Request_Builder_clz.$init.overload(); - okhttp3_Request_Builder_clz_init_uw3i.implementation = function() { - var executor = this.hashCode(); - var beatText = 'public okhttp3.Request$Builder()'; - var beat = newMethodBeat(beatText, executor); - var returnObj = okhttp3_Request_Builder_clz_init_uw3i.call(this); - printBeat(beat); - return returnObj; - }; - var okhttp3_Request_Builder_clz_init_e58t = okhttp3_Request_Builder_clz.$init.overload('okhttp3.Request'); - okhttp3_Request_Builder_clz_init_e58t.implementation = function(v0) { - var executor = this.hashCode(); - var beatText = 'okhttp3.Request$Builder(okhttp3.Request)'; - var beat = newMethodBeat(beatText, executor); - var returnObj = okhttp3_Request_Builder_clz_init_e58t.call(this, v0); - printBeat(beat); - return returnObj; - }; -}); -``` -*** - -### 9. List all available Frida scripts -```shell -某皮 > ls -just_trust_me.js empty.js keystore_dump.js -okhttp3.Request.Builder.addHeader.js edit_text.js activity_events.js -find_boringssl_custom_verify_func.js ssl_log.js hook_register_natives.js -click.js get_device_info.js apk_shell_scanner.js -dump_dex.js object_store.js hook_artmethod_register.js -replace_dlsym_get_pthread_create.js just_trust_me_for_ios.js trace_initproc.js -android_ui.js jni_method_trace.js url.js -just_trust_me_okhttp_hook_finder_for_android.js text_view.js find_anit_frida_so.js -某皮 > -``` -*** - -### 10. Attach and execute a specific Frida script - -> **Auto-logging**: When executing scripts via `attach` / `frida`, Frida `send()` output is automatically saved to a `.log` file with the same name in the app working directory. e.g., `attach url.js` generates `url.log`, making it easy to feed into AI for analysis. - -```shell -某信拍 > attach url.js -Frida output logging -> com.uxin.buyerphone/url.log -------------startFlag:0755liv1,objectHash:-915348569,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836814835--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836814832&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -public java.net.URL(String) - at java.net.URL.(Native Method) - at com.wxbx.wmda.e.b.a(SourceFile:5) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:0755liv1,usedtime:1--------------- - -------------startFlag:1ps6go99,objectHash:-237375819,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815192--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -public java.net.URL(String) - at java.net.URL.(Native Method) - at com.android.okhttp.HttpUrl.url(HttpUrl.java:327) - at com.android.okhttp.Request.url(Request.java:53) - at com.android.okhttp.Request$Builder.build(Native Method) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258) - at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) - at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26) - at com.wxbx.wmda.e.b.a(SourceFile:14) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:1ps6go99,usedtime:0--------------- -// 这里省略无数日志............. -------------startFlag:i7osxvjl,objectHash:134280600,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815193--------------- -url:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0×tamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d -com.android.okhttp.Request.Builder.build() - at com.android.okhttp.Request$Builder.build(Native Method) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124) - at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258) - at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) - at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26) - at com.wxbx.wmda.e.b.a(SourceFile:14) - at com.wxbx.wmda.e.b.a(SourceFile:1) - at com.wxbx.wmda.h.a.a(SourceFile:162) - at com.wxbx.wmda.h.a.b(SourceFile:19) - at com.wxbx.wmda.h.a.a(SourceFile:2) - at com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3) - at android.os.Handler.dispatchMessage(Handler.java:106) - at android.os.Looper.loop(Looper.java:201) - at android.os.HandlerThread.run(HandlerThread.java:65) -------------endFlag:i7osxvjl,usedtime:1--------------- -// 这里省略无数日志............. -``` -*** - -### 11. Set a SOCKS5 proxy for the app -```shell -某音 > proxy socks5://10.112.99.11:9998 -proxy socks5://10.112.99.11:9998 OK -某音 > -``` -*** - - -### 12. Run JustTrustMe to disable all SSL pinning (including boringssl) - -```shell -某音 > justtrustme -Package name: com.ss.xxxx.xxx.aweme -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked! -okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked! -// 这里省略无数日志............. -``` -*** - - -### 13. Spawn the app and execute a specific Frida script - -> **Auto-logging**: `spawn` / `fridaf` also auto-saves Frida `send()` output to a `.log` file with the same name. e.g., `spawn just_trust_me.js` generates `just_trust_me.log`. - -```shell -某信拍 > spawn just_trust_me.js -Frida output logging -> com.uxin.buyerphone/just_trust_me.log -Package name: com.xxx.buyxxphone -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked! -javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked! -// 这里省略无数日志............. -``` -*** - - -### 14. Clear the proxy settings -```shell -某音 > unproxy -unproxy OK -某音 > -``` -*** - - - -### 15. Restart the app - -```shell -某信拍 > restart -restarts com.xxx.buyxxphone -``` -*** - - -### 16. Get UID and PID -```shell -某信拍 > uid -10189 -某信拍 > pid -3509 -``` -*** diff --git "a/app\346\263\250\345\205\245js\350\247\243\345\206\263\346\273\221\345\235\227.md" "b/app\346\263\250\345\205\245js\350\247\243\345\206\263\346\273\221\345\235\227.md" new file mode 100644 index 0000000..bc6ebc6 --- /dev/null +++ "b/app\346\263\250\345\205\245js\350\247\243\345\206\263\346\273\221\345\235\227.md" @@ -0,0 +1,126 @@ +## 关于如何对于app中的js调试 + +### 1.应用场景 + +我们在爬取数据的过程中难免会碰到的一些`滑块`.而这些滑块web我们可以用我debug大法随意调试模拟他滑动上报提交.很多同学对于嵌入在app在的滑块有时候就束手无策了,这时候我们引入了vconsole进行注入爬取 + +### 2.使用前提 + +你必须`web端`调试通过,直接这滑块的js注入点在哪里,如果不知道后续调试难度会增加. + +### 3.使用 + +#### 原理 + +```python +1.利用vconsole生成一个终端调试界面 +2.利用拦包工具对于滑块返回的html页面中js外链的引入替换成我们修改后具有埋点打印的内容js,以及HTML最开始部分引入vconsole的js +``` + +`实现原理就是如此简单` + +js 链接 + +```Python +https://unpkg.com/vconsole@latest/dist/vconsole.min.js +https://cdn.jsdelivr.net/npm/vconsole@latest/dist/vconsole.min.js +``` + +### 4.持久化 + +有些同学会感觉每次拦包我都要替换太麻烦了有没有什么不用手动替换的自动的 + +面对这个需求我们还是有的 + +用到的工具 + +- [Thor HTTP Sniffer](https://itunes.apple.com/app/id1210562295): HTTP 抓包及断点调试。 +- [vConsole 调试脚本](https://github.com/Tencent/vConsole): A lightweight, extendable front-end developer tool for mobile web page. +- [Eruda 调试脚本](https://github.com/liriliri/eruda): Console for mobile browsers. + +#### 第一步:在 Thor 中设置过滤器断点 + +##### 1、打开 Thor, 创建一个过滤器,取名为:”WebView 注入调试”,并添加一个断点进入编辑 + +##### 2、因为需要对 WebView 的响应 HTML 内容进行注入,所以选择在 `请求阶段 > 响应消息体回传前 > 新建匹配规则` + +![匹配规则](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/4.jpg) + +##### » 选择『响应 body 前』 + +![响应 body 前](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/8.jpg) + +##### 3、在匹配规则中加入表达式进行替换,以注入 vConsole 工具 + +**因为对 HTML body 标签注入 js 脚本后可能不会总是生效,所以这里选择优先对 title 标签进行替换** + +加入判断条件:判断是否包含 title 标签 + +``` +@rsp.bodyText CONTAINS[cd] "" +``` + +当有 title 标签时,执行以下表达式 + +```js +^@rsp.bodyText "<\/title>" "" +``` + +没有 title 标签,则找 body 标签替换 + +```js +^@rsp.bodyText "<\/body>" "" +``` + +- 其中`new VConsole()` 是关键不调用不会生成界面 +- 还有载人方式可以使用chalrs或者fiddler替换js再头部注入 +- *考虑加载速度的原因,没有直接使用 github 上的 vConsole 原地址,而是 dump 了一份到 coding.net 仓库里,所以上面用的是 codeing.net 的脚本地址* + +##### » vConsole 注入设置如图 + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/7.jpg) + +**同理,再新建一个断点,完成 Eruda 工具的注入(其实可以不用两个调试脚本都注入,这里只是演示效果)** + +#### 第二步:在 PPHub 中找一个 WebView 界面进行尝试 + +##### 1、打开 Thor > 选择 “WebView 注入调试” 过滤器,并启动 + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/9.jpg) + +##### » 启动抓包 + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/3.jpg) + +##### 2、打开 PPHub + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/2.jpg) + +##### » 并找到一个 WebView, 等待加载完成 (直到出现调试工具入口) + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/1.jpg) + +##### » vConsole 调试效果 + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/6.jpg) + +##### » Eruda 调试效果 + +![img](http://pixelcyber.com/Thor/tips-zh-Hans/bp_res/5.jpg) + +### 参考资料 (排名不分先后) + +- [JSBox 的网页调试插件](https://itunes.apple.com/app/id1312014438) +- [三种解密 HTTPS 流量的方法介绍 by Jerry Qu](https://imququ.com/post/how-to-decrypt-https.html) +- [Content Security Policy Level 2 介绍 by Jerry Qu](https://imququ.com/post/content-security-policy-level-2.html) +- [eruda - Console for mobile browsers](https://eruda.liriliri.io/) +- [vConsole by Tencent](https://github.com/Tencent/vConsole) + +### Thor 20 天免费试用 TestFlight 申请 + +1、[Thor 的详细介绍](https://github.com/PixelCyber/Thor/blob/master/README-zh-Hans.md) + +2、[TestFlight 申请问卷填写](https://wj.qq.com/s/1607760/e57d) + +3、过滤器 “WebView 注入调试” [后期优化版下载](https://github.com/PixelCyber/Thor/raw/master/tips-zh-Hans/bp_res/WebView_debug.f4thor)(下载后直接用 Thor 打开,安装) diff --git a/assets/a.gif b/assets/a.gif new file mode 100755 index 0000000..c1d71bd Binary files /dev/null and b/assets/a.gif differ diff --git a/assets/activity_events.gif b/assets/activity_events.gif new file mode 100755 index 0000000..b929a27 Binary files /dev/null and b/assets/activity_events.gif differ diff --git a/assets/android_ui.gif b/assets/android_ui.gif new file mode 100644 index 0000000..6123f5a Binary files /dev/null and b/assets/android_ui.gif differ diff --git a/assets/android_ui_view_tree.png b/assets/android_ui_view_tree.png new file mode 100644 index 0000000..4616f26 Binary files /dev/null and b/assets/android_ui_view_tree.png differ diff --git a/assets/android_ui_view_tree2.gif b/assets/android_ui_view_tree2.gif new file mode 100644 index 0000000..e194b3a Binary files /dev/null and b/assets/android_ui_view_tree2.gif differ diff --git a/assets/b.gif b/assets/b.gif new file mode 100644 index 0000000..dc7755d Binary files /dev/null and b/assets/b.gif differ diff --git a/assets/c.gif b/assets/c.gif new file mode 100644 index 0000000..487bddb Binary files /dev/null and b/assets/c.gif differ diff --git a/assets/click.gif b/assets/click.gif new file mode 100644 index 0000000..eebea2b Binary files /dev/null and b/assets/click.gif differ diff --git a/assets/d.png b/assets/d.png new file mode 100644 index 0000000..ddca5a7 Binary files /dev/null and b/assets/d.png differ diff --git a/assets/disable_sslpinning.gif b/assets/disable_sslpinning.gif new file mode 100644 index 0000000..c982c5d Binary files /dev/null and b/assets/disable_sslpinning.gif differ diff --git a/assets/dogecoin.jpeg b/assets/dogecoin.jpeg new file mode 100644 index 0000000..497ed1b Binary files /dev/null and b/assets/dogecoin.jpeg differ diff --git a/assets/edit_text.png b/assets/edit_text.png new file mode 100644 index 0000000..89e2797 Binary files /dev/null and b/assets/edit_text.png differ diff --git a/assets/exists_class.gif b/assets/exists_class.gif new file mode 100644 index 0000000..f0ab001 Binary files /dev/null and b/assets/exists_class.gif differ diff --git a/assets/hook_RN.gif b/assets/hook_RN.gif new file mode 100644 index 0000000..1edf536 Binary files /dev/null and b/assets/hook_RN.gif differ diff --git a/assets/hooker-attach.gif b/assets/hooker-attach.gif new file mode 100644 index 0000000..5e50e75 Binary files /dev/null and b/assets/hooker-attach.gif differ diff --git a/assets/hooker-deploy.gif b/assets/hooker-deploy.gif new file mode 100644 index 0000000..83f675d Binary files /dev/null and b/assets/hooker-deploy.gif differ diff --git a/assets/hooking_log.gif b/assets/hooking_log.gif new file mode 100644 index 0000000..5a4cde2 Binary files /dev/null and b/assets/hooking_log.gif differ diff --git a/assets/hooking_url.gif b/assets/hooking_url.gif new file mode 100755 index 0000000..7d27b3c Binary files /dev/null and b/assets/hooking_url.gif differ diff --git a/assets/https_bothway_01.png b/assets/https_bothway_01.png new file mode 100644 index 0000000..3f83191 Binary files /dev/null and b/assets/https_bothway_01.png differ diff --git a/assets/https_bothway_02.png b/assets/https_bothway_02.png new file mode 100644 index 0000000..a29bcf9 Binary files /dev/null and b/assets/https_bothway_02.png differ diff --git a/assets/https_bothway_03.png b/assets/https_bothway_03.png new file mode 100644 index 0000000..17269a1 Binary files /dev/null and b/assets/https_bothway_03.png differ diff --git a/assets/https_bothway_04.png b/assets/https_bothway_04.png new file mode 100644 index 0000000..76b31a2 Binary files /dev/null and b/assets/https_bothway_04.png differ diff --git a/assets/j0.png b/assets/j0.png new file mode 100644 index 0000000..3f66002 Binary files /dev/null and b/assets/j0.png differ diff --git a/assets/j1.png b/assets/j1.png new file mode 100644 index 0000000..0399703 Binary files /dev/null and b/assets/j1.png differ diff --git a/assets/j2.png b/assets/j2.png new file mode 100644 index 0000000..1fc0818 Binary files /dev/null and b/assets/j2.png differ diff --git a/assets/just_trust_me_show.gif b/assets/just_trust_me_show.gif new file mode 100755 index 0000000..831e071 Binary files /dev/null and b/assets/just_trust_me_show.gif differ diff --git a/assets/keystore_dump.png b/assets/keystore_dump.png new file mode 100644 index 0000000..572b5a3 Binary files /dev/null and b/assets/keystore_dump.png differ diff --git a/assets/kill.gif b/assets/kill.gif new file mode 100644 index 0000000..cc12ca1 Binary files /dev/null and b/assets/kill.gif differ diff --git a/assets/kva.jpeg b/assets/kva.jpeg new file mode 100644 index 0000000..3e3e30d Binary files /dev/null and b/assets/kva.jpeg differ diff --git a/assets/objection.gif b/assets/objection.gif new file mode 100644 index 0000000..5114f3b Binary files /dev/null and b/assets/objection.gif differ diff --git a/assets/okhttp_justhook.png b/assets/okhttp_justhook.png new file mode 100644 index 0000000..7bb6ddc Binary files /dev/null and b/assets/okhttp_justhook.png differ diff --git a/assets/qutoutiao.png b/assets/qutoutiao.png new file mode 100644 index 0000000..71361a4 Binary files /dev/null and b/assets/qutoutiao.png differ diff --git a/assets/remote_adb.gif b/assets/remote_adb.gif new file mode 100644 index 0000000..7924257 Binary files /dev/null and b/assets/remote_adb.gif differ diff --git a/assets/s.png b/assets/s.png new file mode 100644 index 0000000..af32ccc Binary files /dev/null and b/assets/s.png differ diff --git a/assets/strat_just_trust_me.gif b/assets/strat_just_trust_me.gif new file mode 100755 index 0000000..fa68fb1 Binary files /dev/null and b/assets/strat_just_trust_me.gif differ diff --git a/assets/text_view.png b/assets/text_view.png new file mode 100644 index 0000000..e37519b Binary files /dev/null and b/assets/text_view.png differ diff --git a/assets/tools_env.gif b/assets/tools_env.gif new file mode 100644 index 0000000..8337c16 Binary files /dev/null and b/assets/tools_env.gif differ diff --git a/assets/v.png b/assets/v.png new file mode 100644 index 0000000..d064ded Binary files /dev/null and b/assets/v.png differ diff --git a/assets/xinit_files.png b/assets/xinit_files.png new file mode 100644 index 0000000..db4db30 Binary files /dev/null and b/assets/xinit_files.png differ diff --git a/assets/xinitdeploy.gif b/assets/xinitdeploy.gif new file mode 100644 index 0000000..65b1dba Binary files /dev/null and b/assets/xinitdeploy.gif differ diff --git a/colorful.py b/colorful.py new file mode 100644 index 0000000..65b06b0 --- /dev/null +++ b/colorful.py @@ -0,0 +1,30 @@ +''' +Created on 2020年6月6日 + +@author: stephen +''' +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) +#front color +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def green(string): + return withColor(string, Green+30) # Green +def yellow(string): + return withColor(string, Yellow+30) # Yellow +def blue(string): + return withColor(string, Blue+30) # Blue +def magenta(string): + return withColor(string, Magenta+30) # Magenta +def cyan(string): + return withColor(string, Cyan+30) # Cyan +def white(string): + return withColor(string, White+30) # White diff --git a/com.blockchainvault/activity_events.js b/com.blockchainvault/activity_events.js new file mode 100644 index 0000000..471ab90 --- /dev/null +++ b/com.blockchainvault/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.blockchainvault/android_ui.js b/com.blockchainvault/android_ui.js new file mode 100644 index 0000000..20c8be2 --- /dev/null +++ b/com.blockchainvault/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.blockchainvault/attach b/com.blockchainvault/attach new file mode 100755 index 0000000..8e2f637 --- /dev/null +++ b/com.blockchainvault/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.blockchainvault \ No newline at end of file diff --git a/com.blockchainvault/click.js b/com.blockchainvault/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.blockchainvault/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.blockchainvault/edit_text.js b/com.blockchainvault/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.blockchainvault/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.blockchainvault/hook_RN.js b/com.blockchainvault/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.blockchainvault/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.blockchainvault/hooking b/com.blockchainvault/hooking new file mode 100755 index 0000000..8830d21 --- /dev/null +++ b/com.blockchainvault/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.blockchainvault | tee -ai log \ No newline at end of file diff --git a/com.blockchainvault/java_util_HashMap_put.js b/com.blockchainvault/java_util_HashMap_put.js new file mode 100644 index 0000000..8d145e7 --- /dev/null +++ b/com.blockchainvault/java_util_HashMap_put.js @@ -0,0 +1,169 @@ +//crack by com.blockchainvault 1.29.0 +//java.util.HashMap:put +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +var log = console.log; + +var print = console.log; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + + + +var loadedXRadar = false; + +function loadXinitDexfile(dexfile) { + loadDexfile('/data/user/0/com.blockchainvault/xinit/'+dexfile); +}; + +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function getPrettyString(javaObject) { + var XPretty = Java.use("gz.util.XPretty"); + return XPretty.getPrettyString(javaObject); +}; + +function xPretty(javaObject) { + var str = getPrettyString(javaObject); + console.log(str); +}; + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + +function storeObjectAndLog(javaObject) { + try { + var className = getClassName(javaObject); + var ObjectsStore = Java.use("gz.radar.objects.ObjectsStore"); + var objectId = ObjectsStore.storeObject(javaObject); + console.log(className + " ObjectsStoreId: " +objectId); + } catch (error) { + console.error("你传的不是一个有效的java对象!"); + } +}; + + + +//java.util.HashMap:put +Java.perform(function() { + var java_util_HashMap_clz = Java.use('java.util.HashMap'); + var java_util_HashMap_clz_method_put_7333 = java_util_HashMap_clz.put.overload('java.lang.Object', 'java.lang.Object'); + java_util_HashMap_clz_method_put_7333.implementation = function(v0, v1) { + + + if (v0 == "captcha_token") { + var executor = this.hashCode(); + var beatText = 'public java.lang.Object java.util.HashMap.put(java.lang.Object,java.lang.Object)'; + var beat = newMethodBeat(beatText, executor); + printBeat(beat); + } + var ret = java_util_HashMap_clz_method_put_7333.call(this, v0, v1); + return ret; + }; +}); \ No newline at end of file diff --git a/com.blockchainvault/just_trust_me.js b/com.blockchainvault/just_trust_me.js new file mode 100644 index 0000000..bd8bc97 --- /dev/null +++ b/com.blockchainvault/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.blockchainvault/just_trust_me_okhttp_hook_finder.js b/com.blockchainvault/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..2748df6 --- /dev/null +++ b/com.blockchainvault/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + continue; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + continue; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + continue; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.blockchainvault/keystore_dump.js b/com.blockchainvault/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.blockchainvault/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.blockchainvault/kill b/com.blockchainvault/kill new file mode 100755 index 0000000..1f8f766 --- /dev/null +++ b/com.blockchainvault/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.blockchainvault \ No newline at end of file diff --git a/com.blockchainvault/object_store.js b/com.blockchainvault/object_store.js new file mode 100644 index 0000000..cc4181d --- /dev/null +++ b/com.blockchainvault/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.blockchainvault/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.blockchainvault/objection b/com.blockchainvault/objection new file mode 100755 index 0000000..a073f6b --- /dev/null +++ b/com.blockchainvault/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.blockchainvault explore \ No newline at end of file diff --git a/com.blockchainvault/spawn b/com.blockchainvault/spawn new file mode 100755 index 0000000..ad0fbca --- /dev/null +++ b/com.blockchainvault/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.blockchainvault -l $1 \ No newline at end of file diff --git a/com.blockchainvault/spider.py b/com.blockchainvault/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.blockchainvault/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.blockchainvault/ssl_log.js b/com.blockchainvault/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.blockchainvault/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.blockchainvault/text_view.js b/com.blockchainvault/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.blockchainvault/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.blockchainvault/url.js b/com.blockchainvault/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.blockchainvault/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.blockchainvault/xinitdeploy b/com.blockchainvault/xinitdeploy new file mode 100755 index 0000000..05e9fd6 --- /dev/null +++ b/com.blockchainvault/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.blockchainvault') \ No newline at end of file diff --git a/com.cubic.autohome/activity_events.js b/com.cubic.autohome/activity_events.js new file mode 100644 index 0000000..4ddb9e5 --- /dev/null +++ b/com.cubic.autohome/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.cubic.autohome/android_ui.js b/com.cubic.autohome/android_ui.js new file mode 100644 index 0000000..8b3a0be --- /dev/null +++ b/com.cubic.autohome/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.cubic.autohome/attach b/com.cubic.autohome/attach new file mode 100755 index 0000000..8906a80 --- /dev/null +++ b/com.cubic.autohome/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.cubic.autohome \ No newline at end of file diff --git a/com.cubic.autohome/click.js b/com.cubic.autohome/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.cubic.autohome/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.cubic.autohome/edit_text.js b/com.cubic.autohome/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.cubic.autohome/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.cubic.autohome/hook_RN.js b/com.cubic.autohome/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.cubic.autohome/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.cubic.autohome/hooking b/com.cubic.autohome/hooking new file mode 100755 index 0000000..9a556dd --- /dev/null +++ b/com.cubic.autohome/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.cubic.autohome | tee -ai log \ No newline at end of file diff --git a/com.cubic.autohome/java_util_Arrays_ArrayList_.js b/com.cubic.autohome/java_util_Arrays_ArrayList_.js new file mode 100644 index 0000000..4d5fe49 --- /dev/null +++ b/com.cubic.autohome/java_util_Arrays_ArrayList_.js @@ -0,0 +1,165 @@ +//crack by com.cubic.autohome 10.19.0 +//java.util.Arrays$ArrayList:_ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +var log = console.log; + +var print = console.log; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + + + +var loadedXRadar = false; + +function loadXinitDexfile(dexfile) { + loadDexfile('/data/user/0/com.cubic.autohome/xinit/'+dexfile); +}; + +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function getPrettyString(javaObject) { + var XPretty = Java.use("gz.util.XPretty"); + return XPretty.getPrettyString(javaObject); +}; + +function xPretty(javaObject) { + var str = getPrettyString(javaObject); + console.log(str); +}; + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + +function storeObjectAndLog(javaObject) { + try { + var className = getClassName(javaObject); + var ObjectsStore = Java.use("gz.radar.objects.ObjectsStore"); + var objectId = ObjectsStore.storeObject(javaObject); + console.log(className + " ObjectsStoreId: " +objectId); + } catch (error) { + console.error("你传的不是一个有效的java对象!"); + } +}; + + + +//java.util.Arrays$ArrayList:_ +Java.perform(function() { + var java_util_Arrays_ArrayList_clz = Java.use('java.util.Arrays$ArrayList'); + var java_util_Arrays_ArrayList_clz_init_5784 = java_util_Arrays_ArrayList_clz.$init.overload('[Ljava.lang.Object;'); + java_util_Arrays_ArrayList_clz_init_5784.implementation = function(v0) { + var executor = this.hashCode(); + var beatText = 'java.util.Arrays$ArrayList(java.lang.Object[])'; + var beat = newMethodBeat(beatText, executor); + var returnObj = java_util_Arrays_ArrayList_clz_init_5784.call(this, v0); + printBeat(beat); + return returnObj; + }; +}); \ No newline at end of file diff --git a/com.cubic.autohome/just_trust_me.js b/com.cubic.autohome/just_trust_me.js new file mode 100644 index 0000000..d990c32 --- /dev/null +++ b/com.cubic.autohome/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.cubic.autohome/just_trust_me_okhttp_hook_finder.js b/com.cubic.autohome/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..e22bf3c --- /dev/null +++ b/com.cubic.autohome/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,69 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +var matchClassNameRegExp = RegExp(/^okhttp3/); + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + continue; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + continue; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + continue; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.cubic.autohome/keystore_dump.js b/com.cubic.autohome/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.cubic.autohome/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.cubic.autohome/kill b/com.cubic.autohome/kill new file mode 100755 index 0000000..85425cc --- /dev/null +++ b/com.cubic.autohome/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.cubic.autohome \ No newline at end of file diff --git a/com.cubic.autohome/object_store.js b/com.cubic.autohome/object_store.js new file mode 100644 index 0000000..7c29328 --- /dev/null +++ b/com.cubic.autohome/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.cubic.autohome/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.cubic.autohome/objection b/com.cubic.autohome/objection new file mode 100755 index 0000000..6beeed3 --- /dev/null +++ b/com.cubic.autohome/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.cubic.autohome explore \ No newline at end of file diff --git a/com.cubic.autohome/spawn b/com.cubic.autohome/spawn new file mode 100755 index 0000000..e1d651c --- /dev/null +++ b/com.cubic.autohome/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.cubic.autohome -l $1 \ No newline at end of file diff --git a/com.cubic.autohome/spider.py b/com.cubic.autohome/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.cubic.autohome/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.cubic.autohome/ssl_log.js b/com.cubic.autohome/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.cubic.autohome/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.cubic.autohome/text_view.js b/com.cubic.autohome/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.cubic.autohome/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.cubic.autohome/url.js b/com.cubic.autohome/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.cubic.autohome/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.cubic.autohome/xinitdeploy b/com.cubic.autohome/xinitdeploy new file mode 100755 index 0000000..22467bf --- /dev/null +++ b/com.cubic.autohome/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.cubic.autohome') \ No newline at end of file diff --git a/com.google.android.youtube/cipher.js b/com.google.android.youtube/cipher.js new file mode 100644 index 0000000..f5afc91 --- /dev/null +++ b/com.google.android.youtube/cipher.js @@ -0,0 +1,797 @@ +//javax.crypto.Cipher:? +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +function methodOutBeat(invokeId, timestamp) { + console.log("------------end:" + invokeId + ",usedtime:" + (new Date().getTime() - timestamp) + "---------------\n"); +} + +function toJSONString(obj) { + try { + var clz = Java.use("com.alibaba.fastjson.JSON"); + var toJSONStringMehtod = clz.toJSONString.overload("java.lang.Object"); + return toJSONStringMehtod.call(clz, obj); + } catch(err) { + console.log("toJSONString:" + err); + } + return ""; +}; + +function logJSONString(obj) { + console.log(toJSONString(obj)); +} + +function log(str) { + console.log(str); +}; + +function discoverClass(className) { + var radarClassResult = undefined; + if (!className) { + return radarClassResult; + } + Java.perform(function() { + var radarClz = Java.use("gz.radar.ClassRadar"); + radarClassResult = radarClz.discoverClass(className); + console.log(radarClassResult.describ()); + }); + return radarClassResult; +}; + +function isSubClass(className, parentClassName) { + var isSubClass = false; + Java.perform(function() { + var radarClz = Java.use("gz.radar.ClassRadar"); + isSubClass = radarClz.isSubClass(className, parentClassName); + }); + return isSubClass; +} + +//查找所有加载的子类 +function findOffspringsClasses(parentClassName, containMethodName) { + var radarResults = new Array(); + //Java.perform(function() { + var index = 0; + var count = 0; + Java.enumerateLoadedClasses({ + onMatch: function(className) { + count++; + if (className.startsWith('[') || className.startsWith('org') || className.startsWith('sun') || className.startsWith('com.android') || className.startsWith('java') || className.startsWith('android') || className.startsWith('dalvik') || className.startsWith('com.google') || className.startsWith('libcore') || className.startsWith('gz.')) { + return; + } + var isSubClass = isSubClass(className, parentClassName); + if (isSubClass) { + var resultItem = discoverClass(className); + if (resultItem && resultItem.containsMethod(containMethodName)) { + radarResults[index] = resultItem; + index++; + } else { + console.log("Do not get the resultItem:" + className); + } + } + }, + onComplete: function() {} + }); + //}); + return radarResults; +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +//根据类名和方法名的正则表达式,匹配所有符合的类 +function scanTargets(scanTargetName, containMethodName) { + var radarResults = new Array(); + Java.perform(function() { + if (classExists(scanTargetName)) { + var resultItem = discoverClass(scanTargetName); + if (resultItem) { + radarResults[0] = resultItem; + } + var offsprings = findOffspringsClasses(scanTargetName, containMethodName); + radarResults = radarResults.concat(offsprings); + } else { + var clzReg = eval("/" + scanTargetName + "/"); + var index = 0; + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (!className) { + console.log(" invalid:" + className); + return; + } + if (clzReg.test(className)) { + var resultItem = discoverClass(className); + if (resultItem && resultItem.containsMethod(containMethodName)) { + radarResults[index] = resultItem; + index++; + } + } + }, + onComplete: function() {} + }); + } + }); + console.log("Discovering done"); + return radarResults; +} + +function generateFridaMethodOverload(clzVarName, radarMethod) { + var overloadJs = ""; + if (!radarMethod.isLocal.value || radarMethod.methodName.value.indexOf("-") > -1) { + return overloadJs; + } + var methodVarName = clzVarName + "_method_" + radarMethod.methodName.value + "_" + Math.random().toString(36).slice( - 4); + overloadJs += "var " + methodVarName + "=" + clzVarName + "." + radarMethod.methodName.value + ".overload("; + if (radarMethod.paramsNum.value > 0) { + for (var j = 0; j < radarMethod.paramsNum.value; j++) { + overloadJs += "'"; + overloadJs += radarMethod.paramsClasses.value[j]; + overloadJs += "'"; + if (j < (radarMethod.paramsNum.value - 1)) { + overloadJs += ","; + } + } + } + overloadJs += ");"; + overloadJs += methodVarName; + overloadJs += ".implementation = function("; + var paramsJs = ""; + for (var j = 0; j < radarMethod.paramsNum.value; j++) { + paramsJs += "v" + j; + if (j < (radarMethod.paramsNum.value - 1)) { + paramsJs += ","; + } + } + overloadJs += paramsJs; + overloadJs += ") {"; + if (radarMethod.returnClass.value != "void") { + overloadJs += "var ret = "; + } + var handle = "this"; + if (radarMethod.isStatic.value) { + handle = clzVarName; + + } + overloadJs += methodVarName + ".call(" + handle; + if (radarMethod.paramsNum.value > 0) { + overloadJs += "," + paramsJs + ");"; + } else { + overloadJs += ");"; + } + overloadJs += "var invokeId = Math.random().toString(36).slice( - 8);"; + overloadJs += "var startTime = new Date().getTime();"; + if (handle == "this") { + overloadJs += "var executor = this.hashCode();"; + } else { + overloadJs += "var executor = '" + clzVarName + "';"; + } + overloadJs += "methodInBeat(invokeId, startTime, '" + radarMethod.describe.value + "', executor);"; + overloadJs += "methodOutBeat(invokeId, startTime);"; + if (radarMethod.returnClass.value != "void") { + overloadJs += "return ret;"; + } + overloadJs += "};"; + return overloadJs; +} + +//生成构造方法的overload +function generateFridaConstructorMethodOverload(clzVarName, constructorMethod) { + var overloadJs = ""; + if (!constructorMethod.isLocal.value) { + return overloadJs; + } + var constructorMethodVarName = clzVarName + "_init_" + Math.random().toString(36).slice( - 4); + var hookConstructorMethodJs = clzVarName + ".$init.overload("; + if (constructorMethod.paramsNum.value > 0) { + for (var j = 0; j < constructorMethod.paramsNum.value; j++) { + hookConstructorMethodJs += "'"; + hookConstructorMethodJs += constructorMethod.paramsClasses.value[j]; + hookConstructorMethodJs += "'"; + if (j < (constructorMethod.paramsNum.value - 1)) { + hookConstructorMethodJs += ","; + } + } + } + hookConstructorMethodJs += ");"; + overloadJs += "var " + constructorMethodVarName + " = " + hookConstructorMethodJs; + overloadJs += constructorMethodVarName + ".implementation = function("; + var paramsJs = ""; + for (var j = 0; j < constructorMethod.paramsNum.value; j++) { + paramsJs += "v" + j; + if (j < (constructorMethod.paramsNum.value - 1)) { + paramsJs += ","; + } + } + overloadJs += paramsJs; + overloadJs += ") {"; + overloadJs += "var obj = "; + overloadJs += constructorMethodVarName + ".call(this"; + if (constructorMethod.paramsNum.value > 0) { + overloadJs += "," + paramsJs + ");"; + } else { + overloadJs += ");"; + } + overloadJs += "var invokeId = Math.random().toString(36).slice( - 8);"; + overloadJs += "var startTime = new Date().getTime();"; + overloadJs += "var executor = this.hashCode();"; + overloadJs += "methodInBeat(invokeId, startTime, '" + constructorMethod.describe.value + "', executor);"; + overloadJs += "methodOutBeat(invokeId, startTime);"; + overloadJs += "return obj;};"; + return overloadJs; +} + +//RadarClassResult string +function generateMethodHookJs(radarClassResult, methodName) { + if (radarClassResult.isEnum.value || radarClassResult.isInterface.value) { + return ""; + } + var hookJs = ""; + var hasHook = false; + var clzHookJs = ""; + + var clzVarName = radarClassResult.className.value.replace(/[\.$;]/g, "_") + "_clz"; + clzHookJs += "var " + clzVarName + " = Java.use('" + radarClassResult.className.value + "');"; + var methods = radarClassResult.methods.value; + var hookAllMethod = methodName == "?"; + var hookAllConstructorMethod = methodName == "_"; + for (var i = 0; i < methods.length; i++) { + var radarMethod = methods[i]; + if (hookAllMethod || radarMethod.matchName(methodName)) { + hasHook = true; + clzHookJs += generateFridaMethodOverload(clzVarName, radarMethod); + } + } + + //是否需要hook构造方法 + if (hookAllConstructorMethod) { + hasHook = true; + var constructorMethods = radarClassResult.constructorMethods.value; + for (var i = 0; i < constructorMethods.length; i++) { + clzHookJs += generateFridaConstructorMethodOverload(clzVarName, constructorMethods[i]); + } + } + + if (hasHook) { + hookJs += clzHookJs; + console.log("generating hook js for class" + radarClassResult.className.value); + } + return hookJs; +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +Java.perform(function() { + var java_security_SecureRandom_clz = Java.use('java.security.SecureRandom'); + var java_security_SecureRandom_clz_method_setSeed_tsea = java_security_SecureRandom_clz.setSeed.overload('long'); + java_security_SecureRandom_clz_method_setSeed_tsea.implementation = function(v0) { + java_security_SecureRandom_clz_method_setSeed_tsea.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public void java.security.SecureRandom.setSeed(long)', executor); + methodOutBeat(invokeId, startTime); + }; + var java_security_SecureRandom_clz_method_setSeed_c9w7 = java_security_SecureRandom_clz.setSeed.overload('[B'); + java_security_SecureRandom_clz_method_setSeed_c9w7.implementation = function(v0) { + java_security_SecureRandom_clz_method_setSeed_c9w7.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public synchronized void java.security.SecureRandom.setSeed(byte[])', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz = Java.use('javax.crypto.Cipher'); + var javax_crypto_Cipher_clz_method_doFinal_std2 = javax_crypto_Cipher_clz.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer'); + javax_crypto_Cipher_clz_method_doFinal_std2.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_doFinal_std2.call(this, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.doFinal(java.nio.ByteBuffer,java.nio.ByteBuffer) throws javax.crypto.ShortBufferException,javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_chy9 = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec'); + javax_crypto_Cipher_clz_method_init_chy9.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_init_chy9.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidKeyException,java.security.InvalidAlgorithmParameterException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_tryTransform_s2x4 = javax_crypto_Cipher_clz.tryTransform.overload('java.security.Key', 'java.security.Provider', 'java.lang.String', '[Ljava.lang.String;', 'javax.crypto.Cipher$NeedToSet'); + javax_crypto_Cipher_clz_method_tryTransform_s2x4.implementation = function(v0, v1, v2, v3, v4) { + var ret = javax_crypto_Cipher_clz_method_tryTransform_s2x4.call(javax_crypto_Cipher_clz, v0, v1, v2, v3, v4); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static org.apache.harmony.security.fortress.Engine$SpiAndProvider javax.crypto.Cipher.tryTransform(java.security.Key,java.security.Provider,java.lang.String,java.lang.String[],javax.crypto.Cipher$NeedToSet)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_da7l = javax_crypto_Cipher_clz.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom'); + javax_crypto_Cipher_clz_method_init_da7l.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_init_da7l.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.cert.Certificate,java.security.SecureRandom) throws java.security.InvalidKeyException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_update_ok86 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int'); + javax_crypto_Cipher_clz_method_update_ok86.implementation = function(v0, v1, v2) { + var ret = javax_crypto_Cipher_clz_method_update_ok86.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[],int,int)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot = javax_crypto_Cipher_clz.getExemptionMechanism.overload(); + javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final javax.crypto.ExemptionMechanism javax.crypto.Cipher.getExemptionMechanism()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_tryCombinations_cyp9 = javax_crypto_Cipher_clz.tryCombinations.overload('java.security.Key', 'java.security.Provider', '[Ljava.lang.String;'); + javax_crypto_Cipher_clz_method_tryCombinations_cyp9.implementation = function(v0, v1, v2) { + var ret = javax_crypto_Cipher_clz_method_tryCombinations_cyp9.call(javax_crypto_Cipher_clz, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static org.apache.harmony.security.fortress.Engine$SpiAndProvider javax.crypto.Cipher.tryCombinations(java.security.Key,java.security.Provider,java.lang.String[])', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_uuhl = javax_crypto_Cipher_clz.init.overload('int', 'java.security.cert.Certificate'); + javax_crypto_Cipher_clz_method_init_uuhl.implementation = function(v0, v1) { + javax_crypto_Cipher_clz_method_init_uuhl.call(this, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.cert.Certificate) throws java.security.InvalidKeyException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_getInstance_ot5d = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String', 'java.lang.String'); + javax_crypto_Cipher_clz_method_getInstance_ot5d.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_getInstance_ot5d.call(javax_crypto_Cipher_clz, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'public static final javax.crypto.Cipher javax.crypto.Cipher.getInstance(java.lang.String,java.lang.String) throws java.security.NoSuchAlgorithmException,java.security.NoSuchProviderException,javax.crypto.NoSuchPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getOutputSize_f23n = javax_crypto_Cipher_clz.getOutputSize.overload('int'); + javax_crypto_Cipher_clz_method_getOutputSize_f23n.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_getOutputSize_f23n.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getOutputSize(int)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_update_tx68 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int', '[B', 'int'); + javax_crypto_Cipher_clz_method_update_tx68.implementation = function(v0, v1, v2, v3, v4) { + var ret = javax_crypto_Cipher_clz_method_update_tx68.call(this, v0, v1, v2, v3, v4); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[],int) throws javax.crypto.ShortBufferException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_doFinal_ihw9 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int', '[B', 'int'); + javax_crypto_Cipher_clz_method_doFinal_ihw9.implementation = function(v0, v1, v2, v3, v4) { + var ret = javax_crypto_Cipher_clz_method_doFinal_ihw9.call(this, v0, v1, v2, v3, v4); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.doFinal(byte[],int,int,byte[],int) throws javax.crypto.ShortBufferException,javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_34ei = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom'); + javax_crypto_Cipher_clz_method_init_34ei.implementation = function(v0, v1, v2, v3) { + javax_crypto_Cipher_clz_method_init_34ei.call(this, v0, v1, v2, v3); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.AlgorithmParameters,java.security.SecureRandom) throws java.security.InvalidKeyException,java.security.InvalidAlgorithmParameterException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_getAlgorithm_lyxb = javax_crypto_Cipher_clz.getAlgorithm.overload(); + javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final java.lang.String javax.crypto.Cipher.getAlgorithm()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_invalidTransformation_ntcg = javax_crypto_Cipher_clz.invalidTransformation.overload('java.lang.String'); + javax_crypto_Cipher_clz_method_invalidTransformation_ntcg.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_invalidTransformation_ntcg.call(javax_crypto_Cipher_clz, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static java.security.NoSuchAlgorithmException javax.crypto.Cipher.invalidTransformation(java.lang.String) throws java.security.NoSuchAlgorithmException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getInstance_oj4j = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String', 'java.security.Provider'); + javax_crypto_Cipher_clz_method_getInstance_oj4j.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_getInstance_oj4j.call(javax_crypto_Cipher_clz, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'public static final javax.crypto.Cipher javax.crypto.Cipher.getInstance(java.lang.String,java.security.Provider) throws java.security.NoSuchAlgorithmException,javax.crypto.NoSuchPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getCipher_2lau = javax_crypto_Cipher_clz.getCipher.overload('java.lang.String', 'java.security.Provider'); + javax_crypto_Cipher_clz_method_getCipher_2lau.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_getCipher_2lau.call(javax_crypto_Cipher_clz, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static javax.crypto.Cipher javax.crypto.Cipher.getCipher(java.lang.String,java.security.Provider) throws java.security.NoSuchAlgorithmException,javax.crypto.NoSuchPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed = javax_crypto_Cipher_clz.checkInputOffsetAndCount.overload('int', 'int', 'int'); + javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed.call(javax_crypto_Cipher_clz, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static void javax.crypto.Cipher.checkInputOffsetAndCount(int,int,int)', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_update_mmvk = javax_crypto_Cipher_clz.update.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer'); + javax_crypto_Cipher_clz_method_update_mmvk.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_update_mmvk.call(this, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(java.nio.ByteBuffer,java.nio.ByteBuffer) throws javax.crypto.ShortBufferException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getProvider_u7pr = javax_crypto_Cipher_clz.getProvider.overload(); + javax_crypto_Cipher_clz_method_getProvider_u7pr.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getProvider_u7pr.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final java.security.Provider javax.crypto.Cipher.getProvider()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_updateAAD_alep = javax_crypto_Cipher_clz.updateAAD.overload('[B', 'int', 'int'); + javax_crypto_Cipher_clz_method_updateAAD_alep.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_updateAAD_alep.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[],int,int)', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh = javax_crypto_Cipher_clz.getMaxAllowedKeyLength.overload('java.lang.String'); + javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh.call(javax_crypto_Cipher_clz, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'public static final int javax.crypto.Cipher.getMaxAllowedKeyLength(java.lang.String) throws java.security.NoSuchAlgorithmException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_update_bto4 = javax_crypto_Cipher_clz.update.overload('[B'); + javax_crypto_Cipher_clz_method_update_bto4.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_update_bto4.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[])', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_doFinal_dxgo = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int', '[B'); + javax_crypto_Cipher_clz_method_doFinal_dxgo.implementation = function(v0, v1, v2, v3) { + var ret = javax_crypto_Cipher_clz_method_doFinal_dxgo.call(this, v0, v1, v2, v3); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.doFinal(byte[],int,int,byte[]) throws javax.crypto.ShortBufferException,javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getInstance_03ug = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String'); + javax_crypto_Cipher_clz_method_getInstance_03ug.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_getInstance_03ug.call(javax_crypto_Cipher_clz, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'public static final javax.crypto.Cipher javax.crypto.Cipher.getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException,javax.crypto.NoSuchPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_u19e = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters'); + javax_crypto_Cipher_clz_method_init_u19e.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_init_u19e.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.AlgorithmParameters) throws java.security.InvalidKeyException,java.security.InvalidAlgorithmParameterException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_updateAAD_ojmm = javax_crypto_Cipher_clz.updateAAD.overload('java.nio.ByteBuffer'); + javax_crypto_Cipher_clz_method_updateAAD_ojmm.implementation = function(v0) { + javax_crypto_Cipher_clz_method_updateAAD_ojmm.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(java.nio.ByteBuffer)', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_doFinal_ixw8 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int'); + javax_crypto_Cipher_clz_method_doFinal_ixw8.implementation = function(v0, v1, v2) { + var ret = javax_crypto_Cipher_clz_method_doFinal_ixw8.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal(byte[],int,int) throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getBlockSize_wjca = javax_crypto_Cipher_clz.getBlockSize.overload(); + javax_crypto_Cipher_clz_method_getBlockSize_wjca.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getBlockSize_wjca.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getBlockSize()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_wrap_05i9 = javax_crypto_Cipher_clz.wrap.overload('java.security.Key'); + javax_crypto_Cipher_clz_method_wrap_05i9.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_wrap_05i9.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.wrap(java.security.Key) throws javax.crypto.IllegalBlockSizeException,java.security.InvalidKeyException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m = javax_crypto_Cipher_clz.getMaxAllowedParameterSpec.overload('java.lang.String'); + javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m.call(javax_crypto_Cipher_clz, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'public static final java.security.spec.AlgorithmParameterSpec javax.crypto.Cipher.getMaxAllowedParameterSpec(java.lang.String) throws java.security.NoSuchAlgorithmException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_checkTransformation_qoag = javax_crypto_Cipher_clz.checkTransformation.overload('java.lang.String'); + javax_crypto_Cipher_clz_method_checkTransformation_qoag.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_checkTransformation_qoag.call(javax_crypto_Cipher_clz, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static [Ljava.lang.String; javax.crypto.Cipher.checkTransformation(java.lang.String) throws java.security.NoSuchAlgorithmException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getParameters_6auy = javax_crypto_Cipher_clz.getParameters.overload(); + javax_crypto_Cipher_clz_method_getParameters_6auy.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getParameters_6auy.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final java.security.AlgorithmParameters javax.crypto.Cipher.getParameters()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getSpi_963r = javax_crypto_Cipher_clz.getSpi.overload('java.security.Key'); + javax_crypto_Cipher_clz_method_getSpi_963r.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_getSpi_963r.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi(java.security.Key)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_doFinal_k1n2 = javax_crypto_Cipher_clz.doFinal.overload('[B'); + javax_crypto_Cipher_clz_method_doFinal_k1n2.implementation = function(v0) { + var ret = javax_crypto_Cipher_clz_method_doFinal_k1n2.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal(byte[]) throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getSpi_vlc2 = javax_crypto_Cipher_clz.getSpi.overload(); + javax_crypto_Cipher_clz_method_getSpi_vlc2.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getSpi_vlc2.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_getIV_2b1b = javax_crypto_Cipher_clz.getIV.overload(); + javax_crypto_Cipher_clz_method_getIV_2b1b.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_getIV_2b1b.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.getIV()', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_unwrap_azqe = javax_crypto_Cipher_clz.unwrap.overload('[B', 'java.lang.String', 'int'); + javax_crypto_Cipher_clz_method_unwrap_azqe.implementation = function(v0, v1, v2) { + var ret = javax_crypto_Cipher_clz_method_unwrap_azqe.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final java.security.Key javax.crypto.Cipher.unwrap(byte[],java.lang.String,int) throws java.security.InvalidKeyException,java.security.NoSuchAlgorithmException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_l66q = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key'); + javax_crypto_Cipher_clz_method_init_l66q.implementation = function(v0, v1) { + javax_crypto_Cipher_clz_method_init_l66q.call(this, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key) throws java.security.InvalidKeyException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_init_w1fs = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom'); + javax_crypto_Cipher_clz_method_init_w1fs.implementation = function(v0, v1, v2, v3) { + javax_crypto_Cipher_clz_method_init_w1fs.call(this, v0, v1, v2, v3); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.spec.AlgorithmParameterSpec,java.security.SecureRandom) throws java.security.InvalidKeyException,java.security.InvalidAlgorithmParameterException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_tryTransformWithProvider_an3a = javax_crypto_Cipher_clz.tryTransformWithProvider.overload('java.security.Key', '[Ljava.lang.String;', 'javax.crypto.Cipher$NeedToSet', 'java.security.Provider$Service'); + javax_crypto_Cipher_clz_method_tryTransformWithProvider_an3a.implementation = function(v0, v1, v2, v3) { + var ret = javax_crypto_Cipher_clz_method_tryTransformWithProvider_an3a.call(javax_crypto_Cipher_clz, v0, v1, v2, v3); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static org.apache.harmony.security.fortress.Engine$SpiAndProvider javax.crypto.Cipher.tryTransformWithProvider(java.security.Key,java.lang.String[],javax.crypto.Cipher$NeedToSet,java.security.Provider$Service)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_checkMode_8g2b = javax_crypto_Cipher_clz.checkMode.overload('int'); + javax_crypto_Cipher_clz_method_checkMode_8g2b.implementation = function(v0) { + javax_crypto_Cipher_clz_method_checkMode_8g2b.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'private void javax.crypto.Cipher.checkMode(int)', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_update_ll68 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int', '[B'); + javax_crypto_Cipher_clz_method_update_ll68.implementation = function(v0, v1, v2, v3) { + var ret = javax_crypto_Cipher_clz_method_update_ll68.call(this, v0, v1, v2, v3); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[]) throws javax.crypto.ShortBufferException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_doFinal_62jl = javax_crypto_Cipher_clz.doFinal.overload(); + javax_crypto_Cipher_clz_method_doFinal_62jl.implementation = function() { + var ret = javax_crypto_Cipher_clz_method_doFinal_62jl.call(this); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal() throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_init_c9qo = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.SecureRandom'); + javax_crypto_Cipher_clz_method_init_c9qo.implementation = function(v0, v1, v2) { + javax_crypto_Cipher_clz_method_init_c9qo.call(this, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.SecureRandom) throws java.security.InvalidKeyException', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_doFinal_rso8 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int'); + javax_crypto_Cipher_clz_method_doFinal_rso8.implementation = function(v0, v1) { + var ret = javax_crypto_Cipher_clz_method_doFinal_rso8.call(this, v0, v1); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.doFinal(byte[],int) throws javax.crypto.IllegalBlockSizeException,javax.crypto.ShortBufferException,javax.crypto.BadPaddingException', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + var javax_crypto_Cipher_clz_method_updateAAD_ry91 = javax_crypto_Cipher_clz.updateAAD.overload('[B'); + javax_crypto_Cipher_clz_method_updateAAD_ry91.implementation = function(v0) { + javax_crypto_Cipher_clz_method_updateAAD_ry91.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[])', executor); + methodOutBeat(invokeId, startTime); + }; + var javax_crypto_Cipher_clz_method_matchAttribute_mv6j = javax_crypto_Cipher_clz.matchAttribute.overload('java.security.Provider$Service', 'java.lang.String', 'java.lang.String'); + javax_crypto_Cipher_clz_method_matchAttribute_mv6j.implementation = function(v0, v1, v2) { + var ret = javax_crypto_Cipher_clz_method_matchAttribute_mv6j.call(javax_crypto_Cipher_clz, v0, v1, v2); + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'javax_crypto_Cipher_clz'; + methodInBeat(invokeId, startTime, 'private static boolean javax.crypto.Cipher.matchAttribute(java.security.Provider$Service,java.lang.String,java.lang.String)', executor); + methodOutBeat(invokeId, startTime); + return ret; + }; + +}); \ No newline at end of file diff --git a/com.google.android.youtube/click.js b/com.google.android.youtube/click.js new file mode 100644 index 0000000..70442e9 --- /dev/null +++ b/com.google.android.youtube/click.js @@ -0,0 +1,48 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.google.android.youtube/edit_text.js b/com.google.android.youtube/edit_text.js new file mode 100644 index 0000000..e6b8053 --- /dev/null +++ b/com.google.android.youtube/edit_text.js @@ -0,0 +1,96 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.google.android.youtube/hooking b/com.google.android.youtube/hooking new file mode 100755 index 0000000..5beeea7 --- /dev/null +++ b/com.google.android.youtube/hooking @@ -0,0 +1,4 @@ +#!/bin/bash +echo "hooking $1" > log +date | tee -ai log +frida -U -l $1 com.google.android.youtube | tee -ai log \ No newline at end of file diff --git a/com.google.android.youtube/interacting b/com.google.android.youtube/interacting new file mode 100755 index 0000000..23443d8 --- /dev/null +++ b/com.google.android.youtube/interacting @@ -0,0 +1,2 @@ +#!/bin/bash +frida -U -l $1 com.google.android.youtube \ No newline at end of file diff --git a/com.google.android.youtube/ipc.js b/com.google.android.youtube/ipc.js new file mode 100644 index 0000000..245bd6b --- /dev/null +++ b/com.google.android.youtube/ipc.js @@ -0,0 +1,92 @@ +function printStacks(androidLogClz, exceptionClz, methodName) { + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log(new Date().getTime()+":"+methodName); + console.log(stackInfo.substring(20)); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var ContextWrapper = Java.use("android.content.ContextWrapper"); + + if (ContextWrapper.sendBroadcast) { + // Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#sendBroadcast(android.content.Intent) + ContextWrapper.sendBroadcast.overload("android.content.Intent").implementation = function(intent) { + console.log("Intent toString: " + intent.toString()); + console.log("Intent getExtras: " + intent.getExtras()); + console.log("Intent getFlags: " + intent.getFlags()); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.sendBroadcast(android.content.Intent)'); + return this.sendBroadcast.overload("android.content.Intent").apply(this, arguments); + }; + + // Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#sendBroadcast(android.content.Intent, java.lang.String) + ContextWrapper.sendBroadcast.overload("android.content.Intent", "java.lang.String").implementation = function(intent, receiverPermission) { + console.log("Intent toString: " + intent.toString()); + console.log("Intent getExtras: " + intent.getExtras()); + console.log("Intent getFlags: " + intent.getFlags()); + console.log("Intent receiverPermission: " + receiverPermission); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.sendBroadcast(android.content.Intent, java.lang.String)'); + return this.sendBroadcast.overload("android.content.Intent", "java.lang.String").apply(this, arguments); + }; + } + + if (ContextWrapper.sendStickyBroadcast) { + // Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#sendStickyBroadcast(android.content.Intent) + ContextWrapper.sendStickyBroadcast.overload("android.content.Intent").implementation = function(intent) { + console.log("Intent toString: " + intent.toString()); + console.log("Intent getExtras: " + intent.getExtras()); + console.log("Intent getFlags: " + intent.getFlags()); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.sendStickyBroadcast(android.content.Intent)'); + return this.sendStickyBroadcast.overload("android.content.Intent").apply(this, arguments); + }; + } + + if (ContextWrapper.startActivity) { + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#startActivity(android.content.Intent) + ContextWrapper.startActivity.overload("android.content.Intent").implementation = function(intent) { + console.log("Intent: " + intent); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.startActivity(android.content.Intent)'); + return this.startActivity.overload("android.content.Intent").apply(this, arguments); + }; + + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#startActivity(android.content.Intent, android.os.Bundle) + ContextWrapper.startActivity.overload("android.content.Intent", "android.os.Bundle").implementation = function(intent, bundle) { + console.log("Intent: " + intent); + console.log("Bundle: " + bundle); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.startActivity(android.content.Intent, android.os.Bundle)'); + return this.startActivity.overload("android.content.Intent", "android.os.Bundle").apply(this, arguments); + }; + } + + if (ContextWrapper.startService) { + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#startService(android.content.Intent) + ContextWrapper.startService.implementation = function(service) { + console.log("startService: " + service); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.startService(android.content.Intent)'); + return this.startService.apply(this, arguments); + }; + } + + if (ContextWrapper.stopService) { + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#stopService(android.content.Intent) + ContextWrapper.stopService.implementation = function(name) { + console.log("stopService: " + name); + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.stopService(android.content.Intent)'); + return this.stopService.apply(this, arguments); + }; + } + + if (ContextWrapper.registerReceiver) { + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter) + ContextWrapper.registerReceiver.overload("android.content.BroadcastReceiver", "android.content.IntentFilter").implementation = function(receiver, filter) { + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)'); + return this.registerReceiver.apply(this, arguments); + }; + + //Ref: https://developer.android.com/reference/android/content/ContextWrapper.html#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler) + ContextWrapper.registerReceiver.overload("android.content.BroadcastReceiver", "android.content.IntentFilter", "java.lang.String", "android.os.Handler").implementation = function(receiver, filter, broadcastPermission, scheduler) { + printStacks(androidLogClz, exceptionClz, 'android.content.ContextWrapper.registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler)'); + return this.registerReceiver.apply(this, arguments); + }; + } +}); \ No newline at end of file diff --git a/com.google.android.youtube/kill b/com.google.android.youtube/kill new file mode 100755 index 0000000..b8c456d --- /dev/null +++ b/com.google.android.youtube/kill @@ -0,0 +1 @@ +frida-kill -U com.google.android.youtube \ No newline at end of file diff --git a/com.google.android.youtube/log.js b/com.google.android.youtube/log.js new file mode 100644 index 0000000..7d20aef --- /dev/null +++ b/com.google.android.youtube/log.js @@ -0,0 +1,9 @@ +Java.perform(function() { + var androidLog = Java.use("android.util.Log"); + var dMethod = androidLog.d.overload("java.lang.String", "java.lang.String"); + dMethod.implementation = function(tag, message) { + var ret = dMethod.call(androidLog, tag, message); + console.log("androidLog.d:"+tag+" message:"+message); + return ret; + }; +}); \ No newline at end of file diff --git a/com.google.android.youtube/text_view.js b/com.google.android.youtube/text_view.js new file mode 100644 index 0000000..55287c4 --- /dev/null +++ b/com.google.android.youtube/text_view.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.google.android.youtube/url.js b/com.google.android.youtube/url.js new file mode 100644 index 0000000..60dbcf3 --- /dev/null +++ b/com.google.android.youtube/url.js @@ -0,0 +1,98 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + // URL + var clz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = clz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var startTime = new Date().getTime(); + var result = urlConstruct.call(this, url); + if (check(url)) { + console.log("url:" + url); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "java.net.URL.$new()", executor); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var startTime = new Date().getTime(); + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + if (check(url)) { + console.log("url:" + url); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "com.android.okhttp.Request.Builder.build()", executor); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var startTime = new Date().getTime(); + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + if (check(url)) { + console.log("url:" + url); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "okhttp3.Request.Builder.build()", executor); + } + return okRequestResult + }; + } +}); \ No newline at end of file diff --git a/com.google.android.youtube/view_pager.js b/com.google.android.youtube/view_pager.js new file mode 100644 index 0000000..a5d3999 --- /dev/null +++ b/com.google.android.youtube/view_pager.js @@ -0,0 +1,1626 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id,name:" + currentThread.getId() +"," + currentThread.getName() + "),timestamp:" + startTime+"---------------"); + console.log(methodName); + console.log(stackInfo.substring(20)); + console.log("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function hookingAndroidV4ViewPager() { + if (!classExists("android.support.v4.view.ViewPager")) { + console.warn("Class android.support.v4.view.ViewPager not found."); + return; + } + var android_support_v4_view_ViewPager_clz = Java.use('android.support.v4.view.ViewPager'); + var android_support_v4_view_ViewPager_clz_method_canScrollHorizontally_72ob = android_support_v4_view_ViewPager_clz.canScrollHorizontally.overload('int'); + android_support_v4_view_ViewPager_clz_method_canScrollHorizontally_72ob.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_canScrollHorizontally_72ob.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.canScrollHorizontally(int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_onSaveInstanceState_95lm = android_support_v4_view_ViewPager_clz.onSaveInstanceState.overload(); + android_support_v4_view_ViewPager_clz_method_onSaveInstanceState_95lm.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_onSaveInstanceState_95lm.call(this); + methodInBeat(invokeId, startTime, 'public android.os.Parcelable android.support.v4.view.ViewPager.onSaveInstanceState()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_onSizeChanged_oomc = android_support_v4_view_ViewPager_clz.onSizeChanged.overload('int', 'int', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_onSizeChanged_oomc.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onSizeChanged_oomc.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onSizeChanged(int,int,int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_pageRight_e3ai = android_support_v4_view_ViewPager_clz.pageRight.overload(); + android_support_v4_view_ViewPager_clz_method_pageRight_e3ai.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_pageRight_e3ai.call(this); + methodInBeat(invokeId, startTime, 'boolean android.support.v4.view.ViewPager.pageRight()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_dispatchKeyEvent_u5sq = android_support_v4_view_ViewPager_clz.dispatchKeyEvent.overload('android.view.KeyEvent'); + android_support_v4_view_ViewPager_clz_method_dispatchKeyEvent_u5sq.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_dispatchKeyEvent_u5sq.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.dispatchKeyEvent(android.view.KeyEvent)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_getAdapter_72hx = android_support_v4_view_ViewPager_clz.getAdapter.overload(); + android_support_v4_view_ViewPager_clz_method_getAdapter_72hx.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getAdapter_72hx.call(this); + methodInBeat(invokeId, startTime, 'public android.support.v4.view.PagerAdapter android.support.v4.view.ViewPager.getAdapter()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_clearOnPageChangeListeners_aa1d = android_support_v4_view_ViewPager_clz.clearOnPageChangeListeners.overload(); + android_support_v4_view_ViewPager_clz_method_clearOnPageChangeListeners_aa1d.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_clearOnPageChangeListeners_aa1d.call(this); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.clearOnPageChangeListeners()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_getChildRectInPagerCoordinates_day6 = android_support_v4_view_ViewPager_clz.getChildRectInPagerCoordinates.overload('android.graphics.Rect', 'android.view.View'); + android_support_v4_view_ViewPager_clz_method_getChildRectInPagerCoordinates_day6.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getChildRectInPagerCoordinates_day6.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'private android.graphics.Rect android.support.v4.view.ViewPager.getChildRectInPagerCoordinates(android.graphics.Rect,android.view.View)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_fakeDragBy_1srx = android_support_v4_view_ViewPager_clz.fakeDragBy.overload('float'); + android_support_v4_view_ViewPager_clz_method_fakeDragBy_1srx.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_fakeDragBy_1srx.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.fakeDragBy(float)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_dataSetChanged_rqbg = android_support_v4_view_ViewPager_clz.dataSetChanged.overload(); + android_support_v4_view_ViewPager_clz_method_dataSetChanged_rqbg.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_dataSetChanged_rqbg.call(this); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.dataSetChanged()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onLayout_cie4 = android_support_v4_view_ViewPager_clz.onLayout.overload('boolean', 'int', 'int', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_onLayout_cie4.implementation = function(v0, v1, v2, v3, v4) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onLayout_cie4.call(this, v0, v1, v2, v3, v4); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onLayout(boolean,int,int,int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_1tkm = android_support_v4_view_ViewPager_clz.setCurrentItemInternal.overload('int', 'boolean', 'boolean', 'int'); + android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_1tkm.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_1tkm.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.setCurrentItemInternal(int,boolean,boolean,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setCurrentItem_9toq = android_support_v4_view_ViewPager_clz.setCurrentItem.overload('int'); + android_support_v4_view_ViewPager_clz_method_setCurrentItem_9toq.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setCurrentItem_9toq.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setCurrentItem(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_pageScrolled_dj79 = android_support_v4_view_ViewPager_clz.pageScrolled.overload('int'); + android_support_v4_view_ViewPager_clz_method_pageScrolled_dj79.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_pageScrolled_dj79.call(this, v0); + methodInBeat(invokeId, startTime, 'private boolean android.support.v4.view.ViewPager.pageScrolled(int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_addOnPageChangeListener_hfn8 = android_support_v4_view_ViewPager_clz.addOnPageChangeListener.overload('android.support.v4.view.ViewPager$OnPageChangeListener'); + android_support_v4_view_ViewPager_clz_method_addOnPageChangeListener_hfn8.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_addOnPageChangeListener_hfn8.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.addOnPageChangeListener(android.support.v4.view.ViewPager$OnPageChangeListener)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_3afb = android_support_v4_view_ViewPager_clz.dispatchPopulateAccessibilityEvent.overload('android.view.accessibility.AccessibilityEvent'); + android_support_v4_view_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_3afb.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_3afb.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_setScrollingCacheEnabled_f7e9 = android_support_v4_view_ViewPager_clz.setScrollingCacheEnabled.overload('boolean'); + android_support_v4_view_ViewPager_clz_method_setScrollingCacheEnabled_f7e9.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setScrollingCacheEnabled_f7e9.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.setScrollingCacheEnabled(boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_infoForAnyChild_6efq = android_support_v4_view_ViewPager_clz.infoForAnyChild.overload('android.view.View'); + android_support_v4_view_ViewPager_clz_method_infoForAnyChild_6efq.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_infoForAnyChild_6efq.call(this, v0); + methodInBeat(invokeId, startTime, 'android.support.v4.view.ViewPager$ItemInfo android.support.v4.view.ViewPager.infoForAnyChild(android.view.View)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_pageLeft_38se = android_support_v4_view_ViewPager_clz.pageLeft.overload(); + android_support_v4_view_ViewPager_clz_method_pageLeft_38se.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_pageLeft_38se.call(this); + methodInBeat(invokeId, startTime, 'boolean android.support.v4.view.ViewPager.pageLeft()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_setScrollState_2p3f = android_support_v4_view_ViewPager_clz.setScrollState.overload('int'); + android_support_v4_view_ViewPager_clz_method_setScrollState_2p3f.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setScrollState_2p3f.call(this, v0); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.setScrollState(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_isGutterDrag_f7ru = android_support_v4_view_ViewPager_clz.isGutterDrag.overload('float', 'float'); + android_support_v4_view_ViewPager_clz_method_isGutterDrag_f7ru.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_isGutterDrag_f7ru.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'private boolean android.support.v4.view.ViewPager.isGutterDrag(float,float)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_completeScroll_ec01 = android_support_v4_view_ViewPager_clz.completeScroll.overload('boolean'); + android_support_v4_view_ViewPager_clz_method_completeScroll_ec01.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_completeScroll_ec01.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.completeScroll(boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onRequestFocusInDescendants_fhrj = android_support_v4_view_ViewPager_clz.onRequestFocusInDescendants.overload('int', 'android.graphics.Rect'); + android_support_v4_view_ViewPager_clz_method_onRequestFocusInDescendants_fhrj.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_onRequestFocusInDescendants_fhrj.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected boolean android.support.v4.view.ViewPager.onRequestFocusInDescendants(int,android.graphics.Rect)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_getPageMargin_wpn8 = android_support_v4_view_ViewPager_clz.getPageMargin.overload(); + android_support_v4_view_ViewPager_clz_method_getPageMargin_wpn8.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getPageMargin_wpn8.call(this); + methodInBeat(invokeId, startTime, 'public int android.support.v4.view.ViewPager.getPageMargin()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_removeOnAdapterChangeListener_n3hk = android_support_v4_view_ViewPager_clz.removeOnAdapterChangeListener.overload('android.support.v4.view.ViewPager$OnAdapterChangeListener'); + android_support_v4_view_ViewPager_clz_method_removeOnAdapterChangeListener_n3hk.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_removeOnAdapterChangeListener_n3hk.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.removeOnAdapterChangeListener(android.support.v4.view.ViewPager$OnAdapterChangeListener)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_isFakeDragging_fdzo = android_support_v4_view_ViewPager_clz.isFakeDragging.overload(); + android_support_v4_view_ViewPager_clz_method_isFakeDragging_fdzo.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_isFakeDragging_fdzo.call(this); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.isFakeDragging()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_removeNonDecorViews_svg1 = android_support_v4_view_ViewPager_clz.removeNonDecorViews.overload(); + android_support_v4_view_ViewPager_clz_method_removeNonDecorViews_svg1.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_removeNonDecorViews_svg1.call(this); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.removeNonDecorViews()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_194a = android_support_v4_view_ViewPager_clz.requestParentDisallowInterceptTouchEvent.overload('boolean'); + android_support_v4_view_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_194a.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_194a.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.requestParentDisallowInterceptTouchEvent(boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_getOffscreenPageLimit_mqks = android_support_v4_view_ViewPager_clz.getOffscreenPageLimit.overload(); + android_support_v4_view_ViewPager_clz_method_getOffscreenPageLimit_mqks.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getOffscreenPageLimit_mqks.call(this); + methodInBeat(invokeId, startTime, 'public int android.support.v4.view.ViewPager.getOffscreenPageLimit()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_addTouchables_imkl = android_support_v4_view_ViewPager_clz.addTouchables.overload('java.util.ArrayList'); + android_support_v4_view_ViewPager_clz_method_addTouchables_imkl.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_addTouchables_imkl.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.addTouchables(java.util.ArrayList)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_getClientWidth_b7kq = android_support_v4_view_ViewPager_clz.getClientWidth.overload(); + android_support_v4_view_ViewPager_clz_method_getClientWidth_b7kq.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getClientWidth_b7kq.call(this); + methodInBeat(invokeId, startTime, 'private int android.support.v4.view.ViewPager.getClientWidth()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_generateLayoutParams_bgb5 = android_support_v4_view_ViewPager_clz.generateLayoutParams.overload('android.view.ViewGroup$LayoutParams'); + android_support_v4_view_ViewPager_clz_method_generateLayoutParams_bgb5.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_generateLayoutParams_bgb5.call(this, v0); + methodInBeat(invokeId, startTime, 'protected android.view.ViewGroup$LayoutParams android.support.v4.view.ViewPager.generateLayoutParams(android.view.ViewGroup$LayoutParams)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_drawableStateChanged_4uae = android_support_v4_view_ViewPager_clz.drawableStateChanged.overload(); + android_support_v4_view_ViewPager_clz_method_drawableStateChanged_4uae.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_drawableStateChanged_4uae.call(this); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.drawableStateChanged()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_infoForPosition_xej7 = android_support_v4_view_ViewPager_clz.infoForPosition.overload('int'); + android_support_v4_view_ViewPager_clz_method_infoForPosition_xej7.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_infoForPosition_xej7.call(this, v0); + methodInBeat(invokeId, startTime, 'android.support.v4.view.ViewPager$ItemInfo android.support.v4.view.ViewPager.infoForPosition(int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_scrollToItem_ooah = android_support_v4_view_ViewPager_clz.scrollToItem.overload('int', 'boolean', 'int', 'boolean'); + android_support_v4_view_ViewPager_clz_method_scrollToItem_ooah.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_scrollToItem_ooah.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.scrollToItem(int,boolean,int,boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onTouchEvent_qhgu = android_support_v4_view_ViewPager_clz.onTouchEvent.overload('android.view.MotionEvent'); + android_support_v4_view_ViewPager_clz_method_onTouchEvent_qhgu.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_onTouchEvent_qhgu.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.onTouchEvent(android.view.MotionEvent)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_generateLayoutParams_ftfu = android_support_v4_view_ViewPager_clz.generateLayoutParams.overload('android.util.AttributeSet'); + android_support_v4_view_ViewPager_clz_method_generateLayoutParams_ftfu.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_generateLayoutParams_ftfu.call(this, v0); + methodInBeat(invokeId, startTime, 'public android.view.ViewGroup$LayoutParams android.support.v4.view.ViewPager.generateLayoutParams(android.util.AttributeSet)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_9t8s = android_support_v4_view_ViewPager_clz.setPageMarginDrawable.overload('android.graphics.drawable.Drawable'); + android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_9t8s.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_9t8s.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setPageMarginDrawable(android.graphics.drawable.Drawable)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_determineTargetPage_oalp = android_support_v4_view_ViewPager_clz.determineTargetPage.overload('int', 'float', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_determineTargetPage_oalp.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_determineTargetPage_oalp.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private int android.support.v4.view.ViewPager.determineTargetPage(int,float,int,int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_resetTouch_moyn = android_support_v4_view_ViewPager_clz.resetTouch.overload(); + android_support_v4_view_ViewPager_clz_method_resetTouch_moyn.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_resetTouch_moyn.call(this); + methodInBeat(invokeId, startTime, 'private boolean android.support.v4.view.ViewPager.resetTouch()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_endFakeDrag_y9y4 = android_support_v4_view_ViewPager_clz.endFakeDrag.overload(); + android_support_v4_view_ViewPager_clz_method_endFakeDrag_y9y4.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_endFakeDrag_y9y4.call(this); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.endFakeDrag()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_populate_0445 = android_support_v4_view_ViewPager_clz.populate.overload(); + android_support_v4_view_ViewPager_clz_method_populate_0445.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_populate_0445.call(this); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.populate()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setOffscreenPageLimit_nmyt = android_support_v4_view_ViewPager_clz.setOffscreenPageLimit.overload('int'); + android_support_v4_view_ViewPager_clz_method_setOffscreenPageLimit_nmyt.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setOffscreenPageLimit_nmyt.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setOffscreenPageLimit(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_getCurrentItem_98is = android_support_v4_view_ViewPager_clz.getCurrentItem.overload(); + android_support_v4_view_ViewPager_clz_method_getCurrentItem_98is.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getCurrentItem_98is.call(this); + methodInBeat(invokeId, startTime, 'public int android.support.v4.view.ViewPager.getCurrentItem()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_onAttachedToWindow_kwdd = android_support_v4_view_ViewPager_clz.onAttachedToWindow.overload(); + android_support_v4_view_ViewPager_clz_method_onAttachedToWindow_kwdd.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onAttachedToWindow_kwdd.call(this); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onAttachedToWindow()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setAdapter_xkla = android_support_v4_view_ViewPager_clz.setAdapter.overload('android.support.v4.view.PagerAdapter'); + android_support_v4_view_ViewPager_clz_method_setAdapter_xkla.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setAdapter_xkla.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setAdapter(android.support.v4.view.PagerAdapter)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_performDrag_59d7 = android_support_v4_view_ViewPager_clz.performDrag.overload('float'); + android_support_v4_view_ViewPager_clz_method_performDrag_59d7.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_performDrag_59d7.call(this, v0); + methodInBeat(invokeId, startTime, 'private boolean android.support.v4.view.ViewPager.performDrag(float)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_isDecorView_n7k5 = android_support_v4_view_ViewPager_clz.isDecorView.overload('android.view.View'); + android_support_v4_view_ViewPager_clz_method_isDecorView_n7k5.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'Class'; + var ret = android_support_v4_view_ViewPager_clz_method_isDecorView_n7k5.call(android_support_v4_view_ViewPager_clz, v0); + methodInBeat(invokeId, startTime, 'private static boolean android.support.v4.view.ViewPager.isDecorView(android.view.View)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_setPageMargin_fwzk = android_support_v4_view_ViewPager_clz.setPageMargin.overload('int'); + android_support_v4_view_ViewPager_clz_method_setPageMargin_fwzk.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setPageMargin_fwzk.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setPageMargin(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_removeView_bj1a = android_support_v4_view_ViewPager_clz.removeView.overload('android.view.View'); + android_support_v4_view_ViewPager_clz_method_removeView_bj1a.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_removeView_bj1a.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.removeView(android.view.View)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_verifyDrawable_k5us = android_support_v4_view_ViewPager_clz.verifyDrawable.overload('android.graphics.drawable.Drawable'); + android_support_v4_view_ViewPager_clz_method_verifyDrawable_k5us.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_verifyDrawable_k5us.call(this, v0); + methodInBeat(invokeId, startTime, 'protected boolean android.support.v4.view.ViewPager.verifyDrawable(android.graphics.drawable.Drawable)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_onDetachedFromWindow_dkyj = android_support_v4_view_ViewPager_clz.onDetachedFromWindow.overload(); + android_support_v4_view_ViewPager_clz_method_onDetachedFromWindow_dkyj.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onDetachedFromWindow_dkyj.call(this); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onDetachedFromWindow()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_populate_h8aj = android_support_v4_view_ViewPager_clz.populate.overload('int'); + android_support_v4_view_ViewPager_clz_method_populate_h8aj.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_populate_h8aj.call(this, v0); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.populate(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_addFocusables_sglh = android_support_v4_view_ViewPager_clz.addFocusables.overload('java.util.ArrayList', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_addFocusables_sglh.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_addFocusables_sglh.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.addFocusables(java.util.ArrayList,int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_infoForChild_1lp9 = android_support_v4_view_ViewPager_clz.infoForChild.overload('android.view.View'); + android_support_v4_view_ViewPager_clz_method_infoForChild_1lp9.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_infoForChild_1lp9.call(this, v0); + methodInBeat(invokeId, startTime, 'android.support.v4.view.ViewPager$ItemInfo android.support.v4.view.ViewPager.infoForChild(android.view.View)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_smoothScrollTo_b5qt = android_support_v4_view_ViewPager_clz.smoothScrollTo.overload('int', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_smoothScrollTo_b5qt.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_smoothScrollTo_b5qt.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.smoothScrollTo(int,int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onMeasure_z925 = android_support_v4_view_ViewPager_clz.onMeasure.overload('int', 'int'); + android_support_v4_view_ViewPager_clz_method_onMeasure_z925.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onMeasure_z925.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onMeasure(int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_sortChildDrawingOrder_k87f = android_support_v4_view_ViewPager_clz.sortChildDrawingOrder.overload(); + android_support_v4_view_ViewPager_clz_method_sortChildDrawingOrder_k87f.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_sortChildDrawingOrder_k87f.call(this); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.sortChildDrawingOrder()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_getChildDrawingOrder_0hcs = android_support_v4_view_ViewPager_clz.getChildDrawingOrder.overload('int', 'int'); + android_support_v4_view_ViewPager_clz_method_getChildDrawingOrder_0hcs.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_getChildDrawingOrder_0hcs.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected int android.support.v4.view.ViewPager.getChildDrawingOrder(int,int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_executeKeyEvent_jvie = android_support_v4_view_ViewPager_clz.executeKeyEvent.overload('android.view.KeyEvent'); + android_support_v4_view_ViewPager_clz_method_executeKeyEvent_jvie.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_executeKeyEvent_jvie.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.executeKeyEvent(android.view.KeyEvent)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_beginFakeDrag_d1ph = android_support_v4_view_ViewPager_clz.beginFakeDrag.overload(); + android_support_v4_view_ViewPager_clz_method_beginFakeDrag_d1ph.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_beginFakeDrag_d1ph.call(this); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.beginFakeDrag()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_endDrag_vk1u = android_support_v4_view_ViewPager_clz.endDrag.overload(); + android_support_v4_view_ViewPager_clz_method_endDrag_vk1u.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_endDrag_vk1u.call(this); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.endDrag()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onDraw_4ser = android_support_v4_view_ViewPager_clz.onDraw.overload('android.graphics.Canvas'); + android_support_v4_view_ViewPager_clz_method_onDraw_4ser.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onDraw_4ser.call(this, v0); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onDraw(android.graphics.Canvas)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setPageTransformer_zkfn = android_support_v4_view_ViewPager_clz.setPageTransformer.overload('boolean', 'android.support.v4.view.ViewPager$PageTransformer'); + android_support_v4_view_ViewPager_clz_method_setPageTransformer_zkfn.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setPageTransformer_zkfn.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setPageTransformer(boolean,android.support.v4.view.ViewPager$PageTransformer)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_recomputeScrollPosition_fqyb = android_support_v4_view_ViewPager_clz.recomputeScrollPosition.overload('int', 'int', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_recomputeScrollPosition_fqyb.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_recomputeScrollPosition_fqyb.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.recomputeScrollPosition(int,int,int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_computeScroll_fcwk = android_support_v4_view_ViewPager_clz.computeScroll.overload(); + android_support_v4_view_ViewPager_clz_method_computeScroll_fcwk.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_computeScroll_fcwk.call(this); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.computeScroll()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_canScroll_0jjk = android_support_v4_view_ViewPager_clz.canScroll.overload('android.view.View', 'boolean', 'int', 'int', 'int'); + android_support_v4_view_ViewPager_clz_method_canScroll_0jjk.implementation = function(v0, v1, v2, v3, v4) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_canScroll_0jjk.call(this, v0, v1, v2, v3, v4); + methodInBeat(invokeId, startTime, 'protected boolean android.support.v4.view.ViewPager.canScroll(android.view.View,boolean,int,int,int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_setOnPageChangeListener_o8n7 = android_support_v4_view_ViewPager_clz.setOnPageChangeListener.overload('android.support.v4.view.ViewPager$OnPageChangeListener'); + android_support_v4_view_ViewPager_clz_method_setOnPageChangeListener_o8n7.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setOnPageChangeListener_o8n7.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setOnPageChangeListener(android.support.v4.view.ViewPager$OnPageChangeListener)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setPageTransformer_x9od = android_support_v4_view_ViewPager_clz.setPageTransformer.overload('boolean', 'android.support.v4.view.ViewPager$PageTransformer', 'int'); + android_support_v4_view_ViewPager_clz_method_setPageTransformer_x9od.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setPageTransformer_x9od.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setPageTransformer(boolean,android.support.v4.view.ViewPager$PageTransformer,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_removeOnPageChangeListener_y6ad = android_support_v4_view_ViewPager_clz.removeOnPageChangeListener.overload('android.support.v4.view.ViewPager$OnPageChangeListener'); + android_support_v4_view_ViewPager_clz_method_removeOnPageChangeListener_y6ad.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_removeOnPageChangeListener_y6ad.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.removeOnPageChangeListener(android.support.v4.view.ViewPager$OnPageChangeListener)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_8ifn = android_support_v4_view_ViewPager_clz.setPageMarginDrawable.overload('int'); + android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_8ifn.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setPageMarginDrawable_8ifn.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setPageMarginDrawable(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_distanceInfluenceForSnapDuration_9iyg = android_support_v4_view_ViewPager_clz.distanceInfluenceForSnapDuration.overload('float'); + android_support_v4_view_ViewPager_clz_method_distanceInfluenceForSnapDuration_9iyg.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_distanceInfluenceForSnapDuration_9iyg.call(this, v0); + methodInBeat(invokeId, startTime, 'float android.support.v4.view.ViewPager.distanceInfluenceForSnapDuration(float)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_initViewPager_sd08 = android_support_v4_view_ViewPager_clz.initViewPager.overload(); + android_support_v4_view_ViewPager_clz_method_initViewPager_sd08.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_initViewPager_sd08.call(this); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.initViewPager()', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setInternalPageChangeListener_pj5b = android_support_v4_view_ViewPager_clz.setInternalPageChangeListener.overload('android.support.v4.view.ViewPager$OnPageChangeListener'); + android_support_v4_view_ViewPager_clz_method_setInternalPageChangeListener_pj5b.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_setInternalPageChangeListener_pj5b.call(this, v0); + methodInBeat(invokeId, startTime, 'android.support.v4.view.ViewPager$OnPageChangeListener android.support.v4.view.ViewPager.setInternalPageChangeListener(android.support.v4.view.ViewPager$OnPageChangeListener)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_onInterceptTouchEvent_vlt5 = android_support_v4_view_ViewPager_clz.onInterceptTouchEvent.overload('android.view.MotionEvent'); + android_support_v4_view_ViewPager_clz_method_onInterceptTouchEvent_vlt5.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_onInterceptTouchEvent_vlt5.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.onInterceptTouchEvent(android.view.MotionEvent)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_arrowScroll_8ntk = android_support_v4_view_ViewPager_clz.arrowScroll.overload('int'); + android_support_v4_view_ViewPager_clz_method_arrowScroll_8ntk.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_arrowScroll_8ntk.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean android.support.v4.view.ViewPager.arrowScroll(int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_addNewItem_5uui = android_support_v4_view_ViewPager_clz.addNewItem.overload('int', 'int'); + android_support_v4_view_ViewPager_clz_method_addNewItem_5uui.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_addNewItem_5uui.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'android.support.v4.view.ViewPager$ItemInfo android.support.v4.view.ViewPager.addNewItem(int,int)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_checkLayoutParams_3hwh = android_support_v4_view_ViewPager_clz.checkLayoutParams.overload('android.view.ViewGroup$LayoutParams'); + android_support_v4_view_ViewPager_clz_method_checkLayoutParams_3hwh.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_checkLayoutParams_3hwh.call(this, v0); + methodInBeat(invokeId, startTime, 'protected boolean android.support.v4.view.ViewPager.checkLayoutParams(android.view.ViewGroup$LayoutParams)', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_dispatchOnScrollStateChanged_29vq = android_support_v4_view_ViewPager_clz.dispatchOnScrollStateChanged.overload('int'); + android_support_v4_view_ViewPager_clz_method_dispatchOnScrollStateChanged_29vq.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_dispatchOnScrollStateChanged_29vq.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.dispatchOnScrollStateChanged(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_enableLayers_rtjj = android_support_v4_view_ViewPager_clz.enableLayers.overload('boolean'); + android_support_v4_view_ViewPager_clz_method_enableLayers_rtjj.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_enableLayers_rtjj.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.enableLayers(boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_draw_luki = android_support_v4_view_ViewPager_clz.draw.overload('android.graphics.Canvas'); + android_support_v4_view_ViewPager_clz_method_draw_luki.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_draw_luki.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.draw(android.graphics.Canvas)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_4la7 = android_support_v4_view_ViewPager_clz.setCurrentItemInternal.overload('int', 'boolean', 'boolean'); + android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_4la7.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setCurrentItemInternal_4la7.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.setCurrentItemInternal(int,boolean,boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_dispatchOnPageSelected_47co = android_support_v4_view_ViewPager_clz.dispatchOnPageSelected.overload('int'); + android_support_v4_view_ViewPager_clz_method_dispatchOnPageSelected_47co.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_dispatchOnPageSelected_47co.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.dispatchOnPageSelected(int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onPageScrolled_ltek = android_support_v4_view_ViewPager_clz.onPageScrolled.overload('int', 'float', 'int'); + android_support_v4_view_ViewPager_clz_method_onPageScrolled_ltek.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onPageScrolled_ltek.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'protected void android.support.v4.view.ViewPager.onPageScrolled(int,float,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onSecondaryPointerUp_6pow = android_support_v4_view_ViewPager_clz.onSecondaryPointerUp.overload('android.view.MotionEvent'); + android_support_v4_view_ViewPager_clz_method_onSecondaryPointerUp_6pow.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onSecondaryPointerUp_6pow.call(this, v0); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.onSecondaryPointerUp(android.view.MotionEvent)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_calculatePageOffsets_zxrk = android_support_v4_view_ViewPager_clz.calculatePageOffsets.overload('android.support.v4.view.ViewPager$ItemInfo', 'int', 'android.support.v4.view.ViewPager$ItemInfo'); + android_support_v4_view_ViewPager_clz_method_calculatePageOffsets_zxrk.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_calculatePageOffsets_zxrk.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.calculatePageOffsets(android.support.v4.view.ViewPager$ItemInfo,int,android.support.v4.view.ViewPager$ItemInfo)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_setCurrentItem_uef6 = android_support_v4_view_ViewPager_clz.setCurrentItem.overload('int', 'boolean'); + android_support_v4_view_ViewPager_clz_method_setCurrentItem_uef6.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_setCurrentItem_uef6.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.setCurrentItem(int,boolean)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_generateDefaultLayoutParams_f0n9 = android_support_v4_view_ViewPager_clz.generateDefaultLayoutParams.overload(); + android_support_v4_view_ViewPager_clz_method_generateDefaultLayoutParams_f0n9.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_generateDefaultLayoutParams_f0n9.call(this); + methodInBeat(invokeId, startTime, 'protected android.view.ViewGroup$LayoutParams android.support.v4.view.ViewPager.generateDefaultLayoutParams()', executor); + return ret; + }; + var android_support_v4_view_ViewPager_clz_method_dispatchOnPageScrolled_ep1v = android_support_v4_view_ViewPager_clz.dispatchOnPageScrolled.overload('int', 'float', 'int'); + android_support_v4_view_ViewPager_clz_method_dispatchOnPageScrolled_ep1v.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_dispatchOnPageScrolled_ep1v.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'private void android.support.v4.view.ViewPager.dispatchOnPageScrolled(int,float,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_addView_eexv = android_support_v4_view_ViewPager_clz.addView.overload('android.view.View', 'int', 'android.view.ViewGroup$LayoutParams'); + android_support_v4_view_ViewPager_clz_method_addView_eexv.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_addView_eexv.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.addView(android.view.View,int,android.view.ViewGroup$LayoutParams)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_addOnAdapterChangeListener_xo79 = android_support_v4_view_ViewPager_clz.addOnAdapterChangeListener.overload('android.support.v4.view.ViewPager$OnAdapterChangeListener'); + android_support_v4_view_ViewPager_clz_method_addOnAdapterChangeListener_xo79.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_addOnAdapterChangeListener_xo79.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.addOnAdapterChangeListener(android.support.v4.view.ViewPager$OnAdapterChangeListener)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_smoothScrollTo_9f5k = android_support_v4_view_ViewPager_clz.smoothScrollTo.overload('int', 'int'); + android_support_v4_view_ViewPager_clz_method_smoothScrollTo_9f5k.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_smoothScrollTo_9f5k.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'void android.support.v4.view.ViewPager.smoothScrollTo(int,int)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_onRestoreInstanceState_yhba = android_support_v4_view_ViewPager_clz.onRestoreInstanceState.overload('android.os.Parcelable'); + android_support_v4_view_ViewPager_clz_method_onRestoreInstanceState_yhba.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + android_support_v4_view_ViewPager_clz_method_onRestoreInstanceState_yhba.call(this, v0); + methodInBeat(invokeId, startTime, 'public void android.support.v4.view.ViewPager.onRestoreInstanceState(android.os.Parcelable)', executor); + }; + var android_support_v4_view_ViewPager_clz_method_infoForCurrentScrollPosition_lyv3 = android_support_v4_view_ViewPager_clz.infoForCurrentScrollPosition.overload(); + android_support_v4_view_ViewPager_clz_method_infoForCurrentScrollPosition_lyv3.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = android_support_v4_view_ViewPager_clz_method_infoForCurrentScrollPosition_lyv3.call(this); + methodInBeat(invokeId, startTime, 'private android.support.v4.view.ViewPager$ItemInfo android.support.v4.view.ViewPager.infoForCurrentScrollPosition()', executor); + return ret; + }; +} + +function hookingAndroidxViewPager() { + if (!classExists("androidx.viewpager.widget.ViewPager")) { + console.warn("Class androidx.viewpager.widget.ViewPager not found."); + return; + } + var androidx_viewpager_widget_ViewPager_clz = Java.use('androidx.viewpager.widget.ViewPager'); + var androidx_viewpager_widget_ViewPager_clz_method_removeOnAdapterChangeListener_346p = androidx_viewpager_widget_ViewPager_clz.removeOnAdapterChangeListener.overload('androidx.viewpager.widget.ViewPager$OnAdapterChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_removeOnAdapterChangeListener_346p.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_removeOnAdapterChangeListener_346p.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.removeOnAdapterChangeListener(androidx.viewpager.widget.ViewPager$OnAdapterChangeListener)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_canScrollHorizontally_tvve = androidx_viewpager_widget_ViewPager_clz.canScrollHorizontally.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_canScrollHorizontally_tvve.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_canScrollHorizontally_tvve.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.canScrollHorizontally(int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_onSaveInstanceState_70eb = androidx_viewpager_widget_ViewPager_clz.onSaveInstanceState.overload(); + androidx_viewpager_widget_ViewPager_clz_method_onSaveInstanceState_70eb.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_onSaveInstanceState_70eb.call(this); + methodInBeat(invokeId, startTime, 'public android.os.Parcelable androidx.viewpager.widget.ViewPager.onSaveInstanceState()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_onSizeChanged_ca5z = androidx_viewpager_widget_ViewPager_clz.onSizeChanged.overload('int', 'int', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_onSizeChanged_ca5z.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onSizeChanged_ca5z.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onSizeChanged(int,int,int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_pageRight_6eur = androidx_viewpager_widget_ViewPager_clz.pageRight.overload(); + androidx_viewpager_widget_ViewPager_clz_method_pageRight_6eur.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_pageRight_6eur.call(this); + methodInBeat(invokeId, startTime, 'boolean androidx.viewpager.widget.ViewPager.pageRight()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_dispatchKeyEvent_v3ew = androidx_viewpager_widget_ViewPager_clz.dispatchKeyEvent.overload('android.view.KeyEvent'); + androidx_viewpager_widget_ViewPager_clz_method_dispatchKeyEvent_v3ew.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_dispatchKeyEvent_v3ew.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.dispatchKeyEvent(android.view.KeyEvent)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_getAdapter_pu8g = androidx_viewpager_widget_ViewPager_clz.getAdapter.overload(); + androidx_viewpager_widget_ViewPager_clz_method_getAdapter_pu8g.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getAdapter_pu8g.call(this); + methodInBeat(invokeId, startTime, 'public androidx.viewpager.widget.PagerAdapter androidx.viewpager.widget.ViewPager.getAdapter()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_clearOnPageChangeListeners_o8up = androidx_viewpager_widget_ViewPager_clz.clearOnPageChangeListeners.overload(); + androidx_viewpager_widget_ViewPager_clz_method_clearOnPageChangeListeners_o8up.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_clearOnPageChangeListeners_o8up.call(this); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.clearOnPageChangeListeners()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_getChildRectInPagerCoordinates_e92s = androidx_viewpager_widget_ViewPager_clz.getChildRectInPagerCoordinates.overload('android.graphics.Rect', 'android.view.View'); + androidx_viewpager_widget_ViewPager_clz_method_getChildRectInPagerCoordinates_e92s.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getChildRectInPagerCoordinates_e92s.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'private android.graphics.Rect androidx.viewpager.widget.ViewPager.getChildRectInPagerCoordinates(android.graphics.Rect,android.view.View)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_fakeDragBy_hq23 = androidx_viewpager_widget_ViewPager_clz.fakeDragBy.overload('float'); + androidx_viewpager_widget_ViewPager_clz_method_fakeDragBy_hq23.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_fakeDragBy_hq23.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.fakeDragBy(float)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_dataSetChanged_6t85 = androidx_viewpager_widget_ViewPager_clz.dataSetChanged.overload(); + androidx_viewpager_widget_ViewPager_clz_method_dataSetChanged_6t85.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_dataSetChanged_6t85.call(this); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.dataSetChanged()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onLayout_bdel = androidx_viewpager_widget_ViewPager_clz.onLayout.overload('boolean', 'int', 'int', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_onLayout_bdel.implementation = function(v0, v1, v2, v3, v4) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onLayout_bdel.call(this, v0, v1, v2, v3, v4); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onLayout(boolean,int,int,int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_ac9m = androidx_viewpager_widget_ViewPager_clz.setCurrentItemInternal.overload('int', 'boolean', 'boolean', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_ac9m.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_ac9m.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.setCurrentItemInternal(int,boolean,boolean,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_g5xa = androidx_viewpager_widget_ViewPager_clz.setCurrentItem.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_g5xa.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_g5xa.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setCurrentItem(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_pageScrolled_rxhe = androidx_viewpager_widget_ViewPager_clz.pageScrolled.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_pageScrolled_rxhe.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_pageScrolled_rxhe.call(this, v0); + methodInBeat(invokeId, startTime, 'private boolean androidx.viewpager.widget.ViewPager.pageScrolled(int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_fhgv = androidx_viewpager_widget_ViewPager_clz.dispatchPopulateAccessibilityEvent.overload('android.view.accessibility.AccessibilityEvent'); + androidx_viewpager_widget_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_fhgv.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_dispatchPopulateAccessibilityEvent_fhgv.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setScrollingCacheEnabled_0y68 = androidx_viewpager_widget_ViewPager_clz.setScrollingCacheEnabled.overload('boolean'); + androidx_viewpager_widget_ViewPager_clz_method_setScrollingCacheEnabled_0y68.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setScrollingCacheEnabled_0y68.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.setScrollingCacheEnabled(boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_infoForAnyChild_3eru = androidx_viewpager_widget_ViewPager_clz.infoForAnyChild.overload('android.view.View'); + androidx_viewpager_widget_ViewPager_clz_method_infoForAnyChild_3eru.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_infoForAnyChild_3eru.call(this, v0); + methodInBeat(invokeId, startTime, 'androidx.viewpager.widget.ViewPager$ItemInfo androidx.viewpager.widget.ViewPager.infoForAnyChild(android.view.View)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_pageLeft_h7dx = androidx_viewpager_widget_ViewPager_clz.pageLeft.overload(); + androidx_viewpager_widget_ViewPager_clz_method_pageLeft_h7dx.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_pageLeft_h7dx.call(this); + methodInBeat(invokeId, startTime, 'boolean androidx.viewpager.widget.ViewPager.pageLeft()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setScrollState_n3qa = androidx_viewpager_widget_ViewPager_clz.setScrollState.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_setScrollState_n3qa.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setScrollState_n3qa.call(this, v0); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.setScrollState(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_isGutterDrag_fyut = androidx_viewpager_widget_ViewPager_clz.isGutterDrag.overload('float', 'float'); + androidx_viewpager_widget_ViewPager_clz_method_isGutterDrag_fyut.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_isGutterDrag_fyut.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'private boolean androidx.viewpager.widget.ViewPager.isGutterDrag(float,float)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_completeScroll_gfqf = androidx_viewpager_widget_ViewPager_clz.completeScroll.overload('boolean'); + androidx_viewpager_widget_ViewPager_clz_method_completeScroll_gfqf.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_completeScroll_gfqf.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.completeScroll(boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onRequestFocusInDescendants_0ydq = androidx_viewpager_widget_ViewPager_clz.onRequestFocusInDescendants.overload('int', 'android.graphics.Rect'); + androidx_viewpager_widget_ViewPager_clz_method_onRequestFocusInDescendants_0ydq.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_onRequestFocusInDescendants_0ydq.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected boolean androidx.viewpager.widget.ViewPager.onRequestFocusInDescendants(int,android.graphics.Rect)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_getPageMargin_80nr = androidx_viewpager_widget_ViewPager_clz.getPageMargin.overload(); + androidx_viewpager_widget_ViewPager_clz_method_getPageMargin_80nr.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getPageMargin_80nr.call(this); + methodInBeat(invokeId, startTime, 'public int androidx.viewpager.widget.ViewPager.getPageMargin()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_calculatePageOffsets_9dlg = androidx_viewpager_widget_ViewPager_clz.calculatePageOffsets.overload('androidx.viewpager.widget.ViewPager$ItemInfo', 'int', 'androidx.viewpager.widget.ViewPager$ItemInfo'); + androidx_viewpager_widget_ViewPager_clz_method_calculatePageOffsets_9dlg.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_calculatePageOffsets_9dlg.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.calculatePageOffsets(androidx.viewpager.widget.ViewPager$ItemInfo,int,androidx.viewpager.widget.ViewPager$ItemInfo)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_isFakeDragging_w9il = androidx_viewpager_widget_ViewPager_clz.isFakeDragging.overload(); + androidx_viewpager_widget_ViewPager_clz_method_isFakeDragging_w9il.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_isFakeDragging_w9il.call(this); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.isFakeDragging()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_removeNonDecorViews_71ul = androidx_viewpager_widget_ViewPager_clz.removeNonDecorViews.overload(); + androidx_viewpager_widget_ViewPager_clz_method_removeNonDecorViews_71ul.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_removeNonDecorViews_71ul.call(this); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.removeNonDecorViews()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_addOnAdapterChangeListener_ucgf = androidx_viewpager_widget_ViewPager_clz.addOnAdapterChangeListener.overload('androidx.viewpager.widget.ViewPager$OnAdapterChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_addOnAdapterChangeListener_ucgf.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_addOnAdapterChangeListener_ucgf.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.addOnAdapterChangeListener(androidx.viewpager.widget.ViewPager$OnAdapterChangeListener)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_5p2g = androidx_viewpager_widget_ViewPager_clz.requestParentDisallowInterceptTouchEvent.overload('boolean'); + androidx_viewpager_widget_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_5p2g.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_requestParentDisallowInterceptTouchEvent_5p2g.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.requestParentDisallowInterceptTouchEvent(boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_getOffscreenPageLimit_5d7d = androidx_viewpager_widget_ViewPager_clz.getOffscreenPageLimit.overload(); + androidx_viewpager_widget_ViewPager_clz_method_getOffscreenPageLimit_5d7d.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getOffscreenPageLimit_5d7d.call(this); + methodInBeat(invokeId, startTime, 'public int androidx.viewpager.widget.ViewPager.getOffscreenPageLimit()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_addTouchables_ay2s = androidx_viewpager_widget_ViewPager_clz.addTouchables.overload('java.util.ArrayList'); + androidx_viewpager_widget_ViewPager_clz_method_addTouchables_ay2s.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_addTouchables_ay2s.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.addTouchables(java.util.ArrayList)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_getClientWidth_g33r = androidx_viewpager_widget_ViewPager_clz.getClientWidth.overload(); + androidx_viewpager_widget_ViewPager_clz_method_getClientWidth_g33r.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getClientWidth_g33r.call(this); + methodInBeat(invokeId, startTime, 'private int androidx.viewpager.widget.ViewPager.getClientWidth()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_m21m = androidx_viewpager_widget_ViewPager_clz.generateLayoutParams.overload('android.view.ViewGroup$LayoutParams'); + androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_m21m.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_m21m.call(this, v0); + methodInBeat(invokeId, startTime, 'protected android.view.ViewGroup$LayoutParams androidx.viewpager.widget.ViewPager.generateLayoutParams(android.view.ViewGroup$LayoutParams)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_46rm = androidx_viewpager_widget_ViewPager_clz.setPageTransformer.overload('boolean', 'androidx.viewpager.widget.ViewPager$PageTransformer'); + androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_46rm.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_46rm.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setPageTransformer(boolean,androidx.viewpager.widget.ViewPager$PageTransformer)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_drawableStateChanged_tacc = androidx_viewpager_widget_ViewPager_clz.drawableStateChanged.overload(); + androidx_viewpager_widget_ViewPager_clz_method_drawableStateChanged_tacc.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_drawableStateChanged_tacc.call(this); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.drawableStateChanged()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_infoForPosition_uo31 = androidx_viewpager_widget_ViewPager_clz.infoForPosition.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_infoForPosition_uo31.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_infoForPosition_uo31.call(this, v0); + methodInBeat(invokeId, startTime, 'androidx.viewpager.widget.ViewPager$ItemInfo androidx.viewpager.widget.ViewPager.infoForPosition(int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_scrollToItem_7u5u = androidx_viewpager_widget_ViewPager_clz.scrollToItem.overload('int', 'boolean', 'int', 'boolean'); + androidx_viewpager_widget_ViewPager_clz_method_scrollToItem_7u5u.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_scrollToItem_7u5u.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.scrollToItem(int,boolean,int,boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onTouchEvent_ml2d = androidx_viewpager_widget_ViewPager_clz.onTouchEvent.overload('android.view.MotionEvent'); + androidx_viewpager_widget_ViewPager_clz_method_onTouchEvent_ml2d.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_onTouchEvent_ml2d.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.onTouchEvent(android.view.MotionEvent)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_o5br = androidx_viewpager_widget_ViewPager_clz.generateLayoutParams.overload('android.util.AttributeSet'); + androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_o5br.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_generateLayoutParams_o5br.call(this, v0); + methodInBeat(invokeId, startTime, 'public android.view.ViewGroup$LayoutParams androidx.viewpager.widget.ViewPager.generateLayoutParams(android.util.AttributeSet)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_0s8r = androidx_viewpager_widget_ViewPager_clz.setPageMarginDrawable.overload('android.graphics.drawable.Drawable'); + androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_0s8r.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_0s8r.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setPageMarginDrawable(android.graphics.drawable.Drawable)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_determineTargetPage_efu8 = androidx_viewpager_widget_ViewPager_clz.determineTargetPage.overload('int', 'float', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_determineTargetPage_efu8.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_determineTargetPage_efu8.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private int androidx.viewpager.widget.ViewPager.determineTargetPage(int,float,int,int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_resetTouch_r3gx = androidx_viewpager_widget_ViewPager_clz.resetTouch.overload(); + androidx_viewpager_widget_ViewPager_clz_method_resetTouch_r3gx.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_resetTouch_r3gx.call(this); + methodInBeat(invokeId, startTime, 'private boolean androidx.viewpager.widget.ViewPager.resetTouch()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_endFakeDrag_3we9 = androidx_viewpager_widget_ViewPager_clz.endFakeDrag.overload(); + androidx_viewpager_widget_ViewPager_clz_method_endFakeDrag_3we9.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_endFakeDrag_3we9.call(this); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.endFakeDrag()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_populate_kzwg = androidx_viewpager_widget_ViewPager_clz.populate.overload(); + androidx_viewpager_widget_ViewPager_clz_method_populate_kzwg.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_populate_kzwg.call(this); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.populate()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setOffscreenPageLimit_ntao = androidx_viewpager_widget_ViewPager_clz.setOffscreenPageLimit.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_setOffscreenPageLimit_ntao.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setOffscreenPageLimit_ntao.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setOffscreenPageLimit(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_getCurrentItem_848m = androidx_viewpager_widget_ViewPager_clz.getCurrentItem.overload(); + androidx_viewpager_widget_ViewPager_clz_method_getCurrentItem_848m.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getCurrentItem_848m.call(this); + methodInBeat(invokeId, startTime, 'public int androidx.viewpager.widget.ViewPager.getCurrentItem()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_onAttachedToWindow_53bs = androidx_viewpager_widget_ViewPager_clz.onAttachedToWindow.overload(); + androidx_viewpager_widget_ViewPager_clz_method_onAttachedToWindow_53bs.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onAttachedToWindow_53bs.call(this); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onAttachedToWindow()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_performDrag_exui = androidx_viewpager_widget_ViewPager_clz.performDrag.overload('float'); + androidx_viewpager_widget_ViewPager_clz_method_performDrag_exui.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_performDrag_exui.call(this, v0); + methodInBeat(invokeId, startTime, 'private boolean androidx.viewpager.widget.ViewPager.performDrag(float)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_isDecorView_nzl4 = androidx_viewpager_widget_ViewPager_clz.isDecorView.overload('android.view.View'); + androidx_viewpager_widget_ViewPager_clz_method_isDecorView_nzl4.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'Class'; + var ret = androidx_viewpager_widget_ViewPager_clz_method_isDecorView_nzl4.call(androidx_viewpager_widget_ViewPager_clz, v0); + methodInBeat(invokeId, startTime, 'private static boolean androidx.viewpager.widget.ViewPager.isDecorView(android.view.View)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setPageMargin_w0hl = androidx_viewpager_widget_ViewPager_clz.setPageMargin.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_setPageMargin_w0hl.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setPageMargin_w0hl.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setPageMargin(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_removeView_c32h = androidx_viewpager_widget_ViewPager_clz.removeView.overload('android.view.View'); + androidx_viewpager_widget_ViewPager_clz_method_removeView_c32h.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_removeView_c32h.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.removeView(android.view.View)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_verifyDrawable_y0h9 = androidx_viewpager_widget_ViewPager_clz.verifyDrawable.overload('android.graphics.drawable.Drawable'); + androidx_viewpager_widget_ViewPager_clz_method_verifyDrawable_y0h9.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_verifyDrawable_y0h9.call(this, v0); + methodInBeat(invokeId, startTime, 'protected boolean androidx.viewpager.widget.ViewPager.verifyDrawable(android.graphics.drawable.Drawable)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_onDetachedFromWindow_k5n2 = androidx_viewpager_widget_ViewPager_clz.onDetachedFromWindow.overload(); + androidx_viewpager_widget_ViewPager_clz_method_onDetachedFromWindow_k5n2.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onDetachedFromWindow_k5n2.call(this); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onDetachedFromWindow()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_removeOnPageChangeListener_5ovh = androidx_viewpager_widget_ViewPager_clz.removeOnPageChangeListener.overload('androidx.viewpager.widget.ViewPager$OnPageChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_removeOnPageChangeListener_5ovh.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_removeOnPageChangeListener_5ovh.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.removeOnPageChangeListener(androidx.viewpager.widget.ViewPager$OnPageChangeListener)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_populate_dtw5 = androidx_viewpager_widget_ViewPager_clz.populate.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_populate_dtw5.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_populate_dtw5.call(this, v0); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.populate(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_addFocusables_w84t = androidx_viewpager_widget_ViewPager_clz.addFocusables.overload('java.util.ArrayList', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_addFocusables_w84t.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_addFocusables_w84t.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.addFocusables(java.util.ArrayList,int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_infoForChild_w2zx = androidx_viewpager_widget_ViewPager_clz.infoForChild.overload('android.view.View'); + androidx_viewpager_widget_ViewPager_clz_method_infoForChild_w2zx.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_infoForChild_w2zx.call(this, v0); + methodInBeat(invokeId, startTime, 'androidx.viewpager.widget.ViewPager$ItemInfo androidx.viewpager.widget.ViewPager.infoForChild(android.view.View)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_zitg = androidx_viewpager_widget_ViewPager_clz.smoothScrollTo.overload('int', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_zitg.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_zitg.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.smoothScrollTo(int,int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onMeasure_ujt8 = androidx_viewpager_widget_ViewPager_clz.onMeasure.overload('int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_onMeasure_ujt8.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onMeasure_ujt8.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onMeasure(int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_sortChildDrawingOrder_no78 = androidx_viewpager_widget_ViewPager_clz.sortChildDrawingOrder.overload(); + androidx_viewpager_widget_ViewPager_clz_method_sortChildDrawingOrder_no78.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_sortChildDrawingOrder_no78.call(this); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.sortChildDrawingOrder()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_getChildDrawingOrder_xmoc = androidx_viewpager_widget_ViewPager_clz.getChildDrawingOrder.overload('int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_getChildDrawingOrder_xmoc.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_getChildDrawingOrder_xmoc.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'protected int androidx.viewpager.widget.ViewPager.getChildDrawingOrder(int,int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_executeKeyEvent_3toa = androidx_viewpager_widget_ViewPager_clz.executeKeyEvent.overload('android.view.KeyEvent'); + androidx_viewpager_widget_ViewPager_clz_method_executeKeyEvent_3toa.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_executeKeyEvent_3toa.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.executeKeyEvent(android.view.KeyEvent)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_beginFakeDrag_e8qj = androidx_viewpager_widget_ViewPager_clz.beginFakeDrag.overload(); + androidx_viewpager_widget_ViewPager_clz_method_beginFakeDrag_e8qj.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_beginFakeDrag_e8qj.call(this); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.beginFakeDrag()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_endDrag_a3zk = androidx_viewpager_widget_ViewPager_clz.endDrag.overload(); + androidx_viewpager_widget_ViewPager_clz_method_endDrag_a3zk.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_endDrag_a3zk.call(this); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.endDrag()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onDraw_2ec1 = androidx_viewpager_widget_ViewPager_clz.onDraw.overload('android.graphics.Canvas'); + androidx_viewpager_widget_ViewPager_clz_method_onDraw_2ec1.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onDraw_2ec1.call(this, v0); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onDraw(android.graphics.Canvas)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_recomputeScrollPosition_vgfe = androidx_viewpager_widget_ViewPager_clz.recomputeScrollPosition.overload('int', 'int', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_recomputeScrollPosition_vgfe.implementation = function(v0, v1, v2, v3) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_recomputeScrollPosition_vgfe.call(this, v0, v1, v2, v3); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.recomputeScrollPosition(int,int,int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_computeScroll_439p = androidx_viewpager_widget_ViewPager_clz.computeScroll.overload(); + androidx_viewpager_widget_ViewPager_clz_method_computeScroll_439p.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_computeScroll_439p.call(this); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.computeScroll()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_canScroll_x6gp = androidx_viewpager_widget_ViewPager_clz.canScroll.overload('android.view.View', 'boolean', 'int', 'int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_canScroll_x6gp.implementation = function(v0, v1, v2, v3, v4) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_canScroll_x6gp.call(this, v0, v1, v2, v3, v4); + methodInBeat(invokeId, startTime, 'protected boolean androidx.viewpager.widget.ViewPager.canScroll(android.view.View,boolean,int,int,int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_b4aj = androidx_viewpager_widget_ViewPager_clz.setPageMarginDrawable.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_b4aj.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setPageMarginDrawable_b4aj.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setPageMarginDrawable(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setOnPageChangeListener_41ur = androidx_viewpager_widget_ViewPager_clz.setOnPageChangeListener.overload('androidx.viewpager.widget.ViewPager$OnPageChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_setOnPageChangeListener_41ur.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setOnPageChangeListener_41ur.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setOnPageChangeListener(androidx.viewpager.widget.ViewPager$OnPageChangeListener)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_distanceInfluenceForSnapDuration_pguu = androidx_viewpager_widget_ViewPager_clz.distanceInfluenceForSnapDuration.overload('float'); + androidx_viewpager_widget_ViewPager_clz_method_distanceInfluenceForSnapDuration_pguu.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_distanceInfluenceForSnapDuration_pguu.call(this, v0); + methodInBeat(invokeId, startTime, 'float androidx.viewpager.widget.ViewPager.distanceInfluenceForSnapDuration(float)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_initViewPager_qtxg = androidx_viewpager_widget_ViewPager_clz.initViewPager.overload(); + androidx_viewpager_widget_ViewPager_clz_method_initViewPager_qtxg.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_initViewPager_qtxg.call(this); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.initViewPager()', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_addOnPageChangeListener_34lc = androidx_viewpager_widget_ViewPager_clz.addOnPageChangeListener.overload('androidx.viewpager.widget.ViewPager$OnPageChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_addOnPageChangeListener_34lc.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_addOnPageChangeListener_34lc.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.addOnPageChangeListener(androidx.viewpager.widget.ViewPager$OnPageChangeListener)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onInterceptTouchEvent_ysyr = androidx_viewpager_widget_ViewPager_clz.onInterceptTouchEvent.overload('android.view.MotionEvent'); + androidx_viewpager_widget_ViewPager_clz_method_onInterceptTouchEvent_ysyr.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_onInterceptTouchEvent_ysyr.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.onInterceptTouchEvent(android.view.MotionEvent)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_arrowScroll_5ub1 = androidx_viewpager_widget_ViewPager_clz.arrowScroll.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_arrowScroll_5ub1.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_arrowScroll_5ub1.call(this, v0); + methodInBeat(invokeId, startTime, 'public boolean androidx.viewpager.widget.ViewPager.arrowScroll(int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_addNewItem_met3 = androidx_viewpager_widget_ViewPager_clz.addNewItem.overload('int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_addNewItem_met3.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_addNewItem_met3.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'androidx.viewpager.widget.ViewPager$ItemInfo androidx.viewpager.widget.ViewPager.addNewItem(int,int)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_checkLayoutParams_4fqh = androidx_viewpager_widget_ViewPager_clz.checkLayoutParams.overload('android.view.ViewGroup$LayoutParams'); + androidx_viewpager_widget_ViewPager_clz_method_checkLayoutParams_4fqh.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_checkLayoutParams_4fqh.call(this, v0); + methodInBeat(invokeId, startTime, 'protected boolean androidx.viewpager.widget.ViewPager.checkLayoutParams(android.view.ViewGroup$LayoutParams)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_setAdapter_ay3i = androidx_viewpager_widget_ViewPager_clz.setAdapter.overload('androidx.viewpager.widget.PagerAdapter'); + androidx_viewpager_widget_ViewPager_clz_method_setAdapter_ay3i.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setAdapter_ay3i.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setAdapter(androidx.viewpager.widget.PagerAdapter)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_dispatchOnScrollStateChanged_ullc = androidx_viewpager_widget_ViewPager_clz.dispatchOnScrollStateChanged.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnScrollStateChanged_ullc.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnScrollStateChanged_ullc.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.dispatchOnScrollStateChanged(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_enableLayers_df5g = androidx_viewpager_widget_ViewPager_clz.enableLayers.overload('boolean'); + androidx_viewpager_widget_ViewPager_clz_method_enableLayers_df5g.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_enableLayers_df5g.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.enableLayers(boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_draw_zkah = androidx_viewpager_widget_ViewPager_clz.draw.overload('android.graphics.Canvas'); + androidx_viewpager_widget_ViewPager_clz_method_draw_zkah.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_draw_zkah.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.draw(android.graphics.Canvas)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_f0v6 = androidx_viewpager_widget_ViewPager_clz.setCurrentItemInternal.overload('int', 'boolean', 'boolean'); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_f0v6.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItemInternal_f0v6.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.setCurrentItemInternal(int,boolean,boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageSelected_41oa = androidx_viewpager_widget_ViewPager_clz.dispatchOnPageSelected.overload('int'); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageSelected_41oa.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageSelected_41oa.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.dispatchOnPageSelected(int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_3q2o = androidx_viewpager_widget_ViewPager_clz.setPageTransformer.overload('boolean', 'androidx.viewpager.widget.ViewPager$PageTransformer', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_3q2o.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setPageTransformer_3q2o.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setPageTransformer(boolean,androidx.viewpager.widget.ViewPager$PageTransformer,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onPageScrolled_qoey = androidx_viewpager_widget_ViewPager_clz.onPageScrolled.overload('int', 'float', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_onPageScrolled_qoey.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onPageScrolled_qoey.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'protected void androidx.viewpager.widget.ViewPager.onPageScrolled(int,float,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onSecondaryPointerUp_85g8 = androidx_viewpager_widget_ViewPager_clz.onSecondaryPointerUp.overload('android.view.MotionEvent'); + androidx_viewpager_widget_ViewPager_clz_method_onSecondaryPointerUp_85g8.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onSecondaryPointerUp_85g8.call(this, v0); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.onSecondaryPointerUp(android.view.MotionEvent)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_m7z9 = androidx_viewpager_widget_ViewPager_clz.setCurrentItem.overload('int', 'boolean'); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_m7z9.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_setCurrentItem_m7z9.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.setCurrentItem(int,boolean)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_generateDefaultLayoutParams_h5km = androidx_viewpager_widget_ViewPager_clz.generateDefaultLayoutParams.overload(); + androidx_viewpager_widget_ViewPager_clz_method_generateDefaultLayoutParams_h5km.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_generateDefaultLayoutParams_h5km.call(this); + methodInBeat(invokeId, startTime, 'protected android.view.ViewGroup$LayoutParams androidx.viewpager.widget.ViewPager.generateDefaultLayoutParams()', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageScrolled_rkqf = androidx_viewpager_widget_ViewPager_clz.dispatchOnPageScrolled.overload('int', 'float', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageScrolled_rkqf.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_dispatchOnPageScrolled_rkqf.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'private void androidx.viewpager.widget.ViewPager.dispatchOnPageScrolled(int,float,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_setInternalPageChangeListener_9cdg = androidx_viewpager_widget_ViewPager_clz.setInternalPageChangeListener.overload('androidx.viewpager.widget.ViewPager$OnPageChangeListener'); + androidx_viewpager_widget_ViewPager_clz_method_setInternalPageChangeListener_9cdg.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_setInternalPageChangeListener_9cdg.call(this, v0); + methodInBeat(invokeId, startTime, 'androidx.viewpager.widget.ViewPager$OnPageChangeListener androidx.viewpager.widget.ViewPager.setInternalPageChangeListener(androidx.viewpager.widget.ViewPager$OnPageChangeListener)', executor); + return ret; + }; + var androidx_viewpager_widget_ViewPager_clz_method_addView_aak3 = androidx_viewpager_widget_ViewPager_clz.addView.overload('android.view.View', 'int', 'android.view.ViewGroup$LayoutParams'); + androidx_viewpager_widget_ViewPager_clz_method_addView_aak3.implementation = function(v0, v1, v2) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_addView_aak3.call(this, v0, v1, v2); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.addView(android.view.View,int,android.view.ViewGroup$LayoutParams)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_hajo = androidx_viewpager_widget_ViewPager_clz.smoothScrollTo.overload('int', 'int'); + androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_hajo.implementation = function(v0, v1) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_smoothScrollTo_hajo.call(this, v0, v1); + methodInBeat(invokeId, startTime, 'void androidx.viewpager.widget.ViewPager.smoothScrollTo(int,int)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_onRestoreInstanceState_v7xe = androidx_viewpager_widget_ViewPager_clz.onRestoreInstanceState.overload('android.os.Parcelable'); + androidx_viewpager_widget_ViewPager_clz_method_onRestoreInstanceState_v7xe.implementation = function(v0) { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + androidx_viewpager_widget_ViewPager_clz_method_onRestoreInstanceState_v7xe.call(this, v0); + methodInBeat(invokeId, startTime, 'public void androidx.viewpager.widget.ViewPager.onRestoreInstanceState(android.os.Parcelable)', executor); + }; + var androidx_viewpager_widget_ViewPager_clz_method_infoForCurrentScrollPosition_zbog = androidx_viewpager_widget_ViewPager_clz.infoForCurrentScrollPosition.overload(); + androidx_viewpager_widget_ViewPager_clz_method_infoForCurrentScrollPosition_zbog.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = this.hashCode(); + var ret = androidx_viewpager_widget_ViewPager_clz_method_infoForCurrentScrollPosition_zbog.call(this); + methodInBeat(invokeId, startTime, 'private androidx.viewpager.widget.ViewPager$ItemInfo androidx.viewpager.widget.ViewPager.infoForCurrentScrollPosition()', executor); + return ret; + }; +} + +Java.perform(function() { + hookingAndroidV4ViewPager(); + hookingAndroidxViewPager(); +}); \ No newline at end of file diff --git a/com.google.android.youtube/web_view.js b/com.google.android.youtube/web_view.js new file mode 100644 index 0000000..0a5ddb4 --- /dev/null +++ b/com.google.android.youtube/web_view.js @@ -0,0 +1,131 @@ +function printStacks(androidLogClz, exceptionClz, methodName) { + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + console.log(new Date().getTime()+":"+methodName); + console.log(stackInfo.substring(20)); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var WebView = Java.use("android.webkit.WebView"); + var WebSettings = Java.use("android.webkit.WebSettings"); + + if (WebView.loadUrl) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#loadUrl(java.lang.String) + WebView.loadUrl.overloads[0].implementation = function(url) { + //checkSettings(this); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.loadUrl(java.lang.String:' + url + ')'); + return this.loadUrl.overloads[0].apply(this, arguments); + }; + + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#loadUrl(java.lang.String, java.util.Map) + WebView.loadUrl.overloads[1].implementation = function(url, additionalHttpHeaders) { + //checkSettings(this); + console.log("WebView Navigation: " + url.toString()); + console.log("WebView Headers: " + additionalHttpHeaders); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.loadUrl(java.lang.String, java.util.Map)'); + return this.loadUrl.overloads[1].apply(this, arguments); + } + } + + if (WebView.loadData) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#loadData(java.lang.String, java.lang.String, java.lang.String) + WebView.loadData.implementation = function(data, mimeType, encoding) { + //checkSettings(this); + console.log("WebView loadData data: " + data); + console.log("WebView loadData mimeType: " + mimeType); + console.log("WebView loadData encoding: " + encoding); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.loadData(java.lang.String, java.lang.String, java.lang.String)'); + return this.loadData.apply(this, arguments); + } + } + + if (WebView.loadDataWithBaseURL) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#loadDataWithBaseURL(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) + WebView.loadDataWithBaseURL.implementation = function(baseUrl, data, mimeType, encoding, historyUrl) { + //checkSettings(this); + console.log("WebView loadDataWithBaseURL baseUrl: " + baseUrl); + console.log("WebView loadDataWithBaseURL data: " + data); + console.log("WebView loadDataWithBaseURL mimeType: " + mimeType); + console.log("WebView loadDataWithBaseURL encoding: " + encoding); + console.log("WebView loadDataWithBaseURL historyUrl: " + historyUrl); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.loadDataWithBaseURL(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)'); + return this.loadDataWithBaseURL.apply(this, arguments); + } + } + + if (WebView.addJavascriptInterface) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String) + WebView.addJavascriptInterface.implementation = function(object, name) { + console.log("addJavascriptInterface Object: " + object.toString()); + console.log("addJavascriptInterface Name: " + name); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.addJavascriptInterface(java.lang.Object, java.lang.String)'); + return this.addJavascriptInterface.apply(this, arguments); + } + } + + if (WebView.evaluateJavascript) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#evaluateJavascript(java.lang.String, android.webkit.ValueCallback) + WebView.evaluateJavascript.implementation = function(script, resultCallback) { + console.log("WebView evaluateJavascript Script: " + script); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.evaluateJavascript(java.lang.String, android.webkit.ValueCallback)'); + return this.evaluateJavascript.apply(this, arguments); + } + } + + if (WebView.postUrl) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#postUrl(java.lang.String, byte[]) + WebView.postUrl.implementation = function(url, postData) { + //checkSettings(this); + console.log("WebView postUrl URL: " + url); + console.log("WebView postUrl postData: " + postData); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.postUrl(java.lang.String, byte[])'); + return this.postUrl.apply(this, arguments); + } + } + + if (WebView.postWebMessage) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#postWebMessage(android.webkit.WebMessage, android.net.Uri) + WebView.postWebMessage.implementation = function(message, targetOrigin) { + console.log("WebView postWebMessage message: " + message.getData()); + console.log("WebView postWebMessage targetOrigin: " + targetOrigin.toString()); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.postWebMessage(android.webkit.WebMessage, android.net.Uri)'); + return this.postWebMessage.apply(this, arguments); + } + } + + if (WebView.savePassword) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#savePassword(java.lang.String, java.lang.String, java.lang.String) + WebView.savePassword.implementation = function(host, username, password) { + console.log("WebView savePassword Host: " + host); + console.log("WebView savePassword Username: " + username); + console.log("WebView savePassword Password: " + password); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.savePassword(java.lang.String, java.lang.String, java.lang.String)'); + return this.savePassword.apply(this, arguments); + } + } + + if (WebView.setHttpAuthUsernamePassword) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String) + WebView.setHttpAuthUsernamePassword.implementation = function(host, realm, username, password) { + console.log("WebView setHttpAuthUsernamePassword Host: " + host); + console.log("WebView setHttpAuthUsernamePassword Realm: " + realm); + console.log("WebView setHttpAuthUsernamePassword Username: " + username); + console.log("WebView setHttpAuthUsernamePassword Password: " + password); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String)'); + return this.setHttpAuthUsernamePassword.apply(this, arguments); + } + } + + if (WebView.getHttpAuthUsernamePassword) { + //Ref: https://developer.android.com/reference/android/webkit/WebView.html#getHttpAuthUsernamePassword(java.lang.String, java.lang.String) + WebView.getHttpAuthUsernamePassword.implementation = function(host, realm) { + var credentials = this.getHttpAuthUsernamePassword.apply(this, arguments); + console.log("WebView getHttpAuthUsernamePassword Host: " + host); + console.log("WebView getHttpAuthUsernamePassword Host: " + realm); + console.log("WebView getHttpAuthUsernamePassword Credentials: " + credentials); + printStacks(androidLogClz, exceptionClz, 'android.webkit.WebView.getHttpAuthUsernamePassword(java.lang.String, java.lang.String)'); + return credentials; + } + } +}); \ No newline at end of file diff --git a/com.google.android.youtube/xinitdeploy b/com.google.android.youtube/xinitdeploy new file mode 100755 index 0000000..810297e --- /dev/null +++ b/com.google.android.youtube/xinitdeploy @@ -0,0 +1,108 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +def readJs(filename): + return io.open('../js/' + filename,'r',encoding= 'utf8').read() + +base_jscode = readJs("base.js") + +radar_jscode = readJs("radar.js") + +jsfmt_jscode = readJs("jsfmt.js") + +rpc_jscode = base_jscode + radar_jscode + jsfmt_jscode + readJs("rpc.js") + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles = xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + try: + rdev = frida.get_usb_device() + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if len(xinitFiles) == 0: + warn("deploying aborted.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + print("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.google.android.youtube') \ No newline at end of file diff --git a/com.gotokeep.keep/activity_events.js b/com.gotokeep.keep/activity_events.js new file mode 100644 index 0000000..e0d808c --- /dev/null +++ b/com.gotokeep.keep/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.gotokeep.keep/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.gotokeep.keep/android_ui.js b/com.gotokeep.keep/android_ui.js new file mode 100644 index 0000000..1f0c3a7 --- /dev/null +++ b/com.gotokeep.keep/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.gotokeep.keep/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.gotokeep.keep/attach b/com.gotokeep.keep/attach new file mode 100755 index 0000000..50bbad2 --- /dev/null +++ b/com.gotokeep.keep/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.gotokeep.keep \ No newline at end of file diff --git a/com.gotokeep.keep/click.js b/com.gotokeep.keep/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.gotokeep.keep/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.gotokeep.keep/disable_sslpinning b/com.gotokeep.keep/disable_sslpinning new file mode 100755 index 0000000..7af3524 --- /dev/null +++ b/com.gotokeep.keep/disable_sslpinning @@ -0,0 +1,13 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +from objection.state.connection import state_connection +from objection.utils.agent import Agent + + +state_connection.gadget_name = "com.gotokeep.keep" +state_connection.agent = Agent() +state_connection.agent.inject() +api = state_connection.get_api() +api.android_ssl_pinning_disable(False) + +inputCommand = input("input any word to quit!!!") diff --git a/com.gotokeep.keep/dump_dex.js b/com.gotokeep.keep/dump_dex.js new file mode 100644 index 0000000..8e1dd4e --- /dev/null +++ b/com.gotokeep.keep/dump_dex.js @@ -0,0 +1,160 @@ +function get_self_process_name() { + var openPtr = Module.getExportByName('libc.so', 'open'); + var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']); + + var readPtr = Module.getExportByName("libc.so", "read"); + var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]); + + var closePtr = Module.getExportByName('libc.so', 'close'); + var close = new NativeFunction(closePtr, 'int', ['int']); + + var path = Memory.allocUtf8String("/proc/self/cmdline"); + var fd = open(path, 0); + if (fd != -1) { + var buffer = Memory.alloc(0x1000); + + var result = read(fd, buffer, 0x1000); + close(fd); + result = ptr(buffer).readCString(); + return result; + } + + return "-1"; +} + + +function mkdir(path) { + var mkdirPtr = Module.getExportByName('libc.so', 'mkdir'); + var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']); + + + + var opendirPtr = Module.getExportByName('libc.so', 'opendir'); + var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']); + + var closedirPtr = Module.getExportByName('libc.so', 'closedir'); + var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']); + + var cPath = Memory.allocUtf8String(path); + var dir = opendir(cPath); + if (dir != 0) { + closedir(dir); + return 0; + } + mkdir(cPath, 755); + chmod(path); +} + +function chmod(path) { + var chmodPtr = Module.getExportByName('libc.so', 'chmod'); + var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']); + var cPath = Memory.allocUtf8String(path); + chmod(cPath, 755); +} + +function dump_dex() { + var libart = Process.findModuleByName("libart.so"); + var addr_DefineClass = null; + var symbols = libart.enumerateSymbols(); + for (var index = 0; index < symbols.length; index++) { + var symbol = symbols[index]; + var symbol_name = symbol.name; + //这个DefineClass的函数签名是Android9的 + //_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE + if (symbol_name.indexOf("ClassLinker") >= 0 && + symbol_name.indexOf("DefineClass") >= 0 && + symbol_name.indexOf("Thread") >= 0 && + symbol_name.indexOf("DexFile") >= 0) { + console.log(symbol_name, symbol.address); + addr_DefineClass = symbol.address; + } + } + var dex_maps = {}; + var dex_count = 1; + + console.log("[DefineClass:]", addr_DefineClass); + if (addr_DefineClass) { + Interceptor.attach(addr_DefineClass, { + onEnter: function(args) { + var dex_file = args[5]; + //ptr(dex_file).add(Process.pointerSize) is "const uint8_t* const begin_;" + //ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is "const size_t size_;" + var base = ptr(dex_file).add(Process.pointerSize).readPointer(); + var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt(); + + if (dex_maps[base] == undefined) { + dex_maps[base] = size; + var magic = ptr(base).readCString(); + if (magic.indexOf("dex") == 0) { + + var process_name = get_self_process_name(); + if (process_name != "-1") { + var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name; + mkdir(dex_dir_path); + var dex_path = dex_dir_path + "/classes" + (dex_count == 1 ? "" : dex_count) + ".dex"; + console.log("[find dex]:", dex_path); + var fd = new File(dex_path, "wb"); + if (fd && fd != null) { + dex_count++; + var dex_buffer = ptr(base).readByteArray(size); + fd.write(dex_buffer); + fd.flush(); + fd.close(); + console.log("[dump dex]:", dex_path); + + } + } + } + } + }, + onLeave: function(retval) {} + }); + } +} + +var is_hook_libart = false; + +function hook_dlopen() { + Interceptor.attach(Module.findExportByName(null, "dlopen"), { + onEnter: function(args) { + var pathptr = args[0]; + if (pathptr !== undefined && pathptr != null) { + var path = ptr(pathptr).readCString(); + //console.log("dlopen:", path); + if (path.indexOf("libart.so") >= 0) { + this.can_hook_libart = true; + console.log("[dlopen:]", path); + } + } + }, + onLeave: function(retval) { + if (this.can_hook_libart && !is_hook_libart) { + dump_dex(); + is_hook_libart = true; + } + } + }) + + Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { + onEnter: function(args) { + var pathptr = args[0]; + if (pathptr !== undefined && pathptr != null) { + var path = ptr(pathptr).readCString(); + //console.log("android_dlopen_ext:", path); + if (path.indexOf("libart.so") >= 0) { + this.can_hook_libart = true; + console.log("[android_dlopen_ext:]", path); + } + } + }, + onLeave: function(retval) { + if (this.can_hook_libart && !is_hook_libart) { + dump_dex(); + is_hook_libart = true; + } + } + }); +} + + +setImmediate(dump_dex); \ No newline at end of file diff --git a/com.gotokeep.keep/edit_text.js b/com.gotokeep.keep/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.gotokeep.keep/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.gotokeep.keep/hook_RN.js b/com.gotokeep.keep/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.gotokeep.keep/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.gotokeep.keep/hooking b/com.gotokeep.keep/hooking new file mode 100755 index 0000000..dd265ec --- /dev/null +++ b/com.gotokeep.keep/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.gotokeep.keep | tee -ai log \ No newline at end of file diff --git a/com.gotokeep.keep/just_trust_me.js b/com.gotokeep.keep/just_trust_me.js new file mode 100644 index 0000000..c7021ef --- /dev/null +++ b/com.gotokeep.keep/just_trust_me.js @@ -0,0 +1,410 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.gotokeep.keep/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl') was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl') was hooked!"); + }; + } + } + + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.ConscryptEngine) throws java.security.cert.CertificateException was hooked!"); + }; + } + } +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(this, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactory.$new(); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = undefined; + try{ + TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckServerTrusted_8813) { + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckServerTrusted_7015 = undefined; + try{ + TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally { + if (TrustManagerImplCheckServerTrusted_7015) { + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_5587 = undefined; + try{ + TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_5587) { + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_9999 = undefined; + try{ + TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_9999) { + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_2872 = undefined; + try{ + TrustManagerImplCheckTrusted_2872 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', '[B', '[B', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_2872) { + TrustManagerImplCheckTrusted_2872.implementation = function(v0, v1, v2, v3, v4, v5) { + console.log('private java.util.List com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean) throws java.security.cert.CertificateException was hooked!'); + return newArrayList(); + }; + } + } + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.gotokeep.keep/just_trust_me_okhttp_hook_finder.js b/com.gotokeep.keep/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..5834140 --- /dev/null +++ b/com.gotokeep.keep/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.gotokeep.keep/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + return; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + return; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + return; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.gotokeep.keep/keystore_dump.js b/com.gotokeep.keep/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.gotokeep.keep/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.gotokeep.keep/kill b/com.gotokeep.keep/kill new file mode 100755 index 0000000..a9cad23 --- /dev/null +++ b/com.gotokeep.keep/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.gotokeep.keep \ No newline at end of file diff --git a/com.gotokeep.keep/object_store.js b/com.gotokeep.keep/object_store.js new file mode 100644 index 0000000..918bac2 --- /dev/null +++ b/com.gotokeep.keep/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.gotokeep.keep/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.gotokeep.keep/objection b/com.gotokeep.keep/objection new file mode 100755 index 0000000..eff1721 --- /dev/null +++ b/com.gotokeep.keep/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.gotokeep.keep explore \ No newline at end of file diff --git a/com.gotokeep.keep/spawn b/com.gotokeep.keep/spawn new file mode 100755 index 0000000..29d89dd --- /dev/null +++ b/com.gotokeep.keep/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.gotokeep.keep -l $1 \ No newline at end of file diff --git a/com.gotokeep.keep/spider.py b/com.gotokeep.keep/spider.py new file mode 100644 index 0000000..1de5ae9 --- /dev/null +++ b/com.gotokeep.keep/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line.strip()); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.gotokeep.keep/ssl_log.js b/com.gotokeep.keep/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.gotokeep.keep/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.gotokeep.keep/text_view.js b/com.gotokeep.keep/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.gotokeep.keep/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.gotokeep.keep/url.js b/com.gotokeep.keep/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.gotokeep.keep/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.gotokeep.keep/xinitdeploy b/com.gotokeep.keep/xinitdeploy new file mode 100755 index 0000000..49cee68 --- /dev/null +++ b/com.gotokeep.keep/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.gotokeep.keep') \ No newline at end of file diff --git a/com.jifen.qukan/activity_events.js b/com.jifen.qukan/activity_events.js new file mode 100644 index 0000000..1704e34 --- /dev/null +++ b/com.jifen.qukan/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.jifen.qukan/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.jifen.qukan/android_ui.js b/com.jifen.qukan/android_ui.js new file mode 100644 index 0000000..9e8dd99 --- /dev/null +++ b/com.jifen.qukan/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.jifen.qukan/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.jifen.qukan/attach b/com.jifen.qukan/attach new file mode 100755 index 0000000..e7484e0 --- /dev/null +++ b/com.jifen.qukan/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.jifen.qukan \ No newline at end of file diff --git a/com.jifen.qukan/click.js b/com.jifen.qukan/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.jifen.qukan/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.jifen.qukan/edit_text.js b/com.jifen.qukan/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.jifen.qukan/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.jifen.qukan/hook_RN.js b/com.jifen.qukan/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.jifen.qukan/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.jifen.qukan/hooking b/com.jifen.qukan/hooking new file mode 100755 index 0000000..0f73b08 --- /dev/null +++ b/com.jifen.qukan/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.jifen.qukan | tee -ai log \ No newline at end of file diff --git a/com.jifen.qukan/just_trust_me.js b/com.jifen.qukan/just_trust_me.js new file mode 100644 index 0000000..16edafa --- /dev/null +++ b/com.jifen.qukan/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.jifen.qukan/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.g")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.g'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.a.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.i.d")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.i.d'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.a.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.jifen.qukan/just_trust_me_okhttp_hook_finder.js b/com.jifen.qukan/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..51d46e4 --- /dev/null +++ b/com.jifen.qukan/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.jifen.qukan/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + return; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + return; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + return; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.jifen.qukan/keystore_dump.js b/com.jifen.qukan/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.jifen.qukan/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.jifen.qukan/kill b/com.jifen.qukan/kill new file mode 100755 index 0000000..2bae71c --- /dev/null +++ b/com.jifen.qukan/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.jifen.qukan \ No newline at end of file diff --git a/com.jifen.qukan/object_store.js b/com.jifen.qukan/object_store.js new file mode 100644 index 0000000..f6a980a --- /dev/null +++ b/com.jifen.qukan/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.jifen.qukan/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.jifen.qukan/objection b/com.jifen.qukan/objection new file mode 100755 index 0000000..24ec65c --- /dev/null +++ b/com.jifen.qukan/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.jifen.qukan explore \ No newline at end of file diff --git a/com.jifen.qukan/spawn b/com.jifen.qukan/spawn new file mode 100755 index 0000000..7e63f57 --- /dev/null +++ b/com.jifen.qukan/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.jifen.qukan -l $1 \ No newline at end of file diff --git a/com.jifen.qukan/spider.py b/com.jifen.qukan/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.jifen.qukan/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.jifen.qukan/ssl_log.js b/com.jifen.qukan/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.jifen.qukan/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.jifen.qukan/text_view.js b/com.jifen.qukan/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.jifen.qukan/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.jifen.qukan/url.js b/com.jifen.qukan/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.jifen.qukan/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.jifen.qukan/xinitdeploy b/com.jifen.qukan/xinitdeploy new file mode 100755 index 0000000..3d7af17 --- /dev/null +++ b/com.jifen.qukan/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.jifen.qukan') \ No newline at end of file diff --git a/com.lwby.breader.ad/activity_events.js b/com.lwby.breader.ad/activity_events.js new file mode 100644 index 0000000..8b7d02d --- /dev/null +++ b/com.lwby.breader.ad/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.lwby.breader.ad/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.lwby.breader.ad/android_ui.js b/com.lwby.breader.ad/android_ui.js new file mode 100644 index 0000000..556e725 --- /dev/null +++ b/com.lwby.breader.ad/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.lwby.breader.ad/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.lwby.breader.ad/attach b/com.lwby.breader.ad/attach new file mode 100755 index 0000000..b4a5082 --- /dev/null +++ b/com.lwby.breader.ad/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.lwby.breader.ad \ No newline at end of file diff --git a/com.lwby.breader.ad/click.js b/com.lwby.breader.ad/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.lwby.breader.ad/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.lwby.breader.ad/edit_text.js b/com.lwby.breader.ad/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.lwby.breader.ad/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.lwby.breader.ad/hook_RN.js b/com.lwby.breader.ad/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.lwby.breader.ad/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.lwby.breader.ad/hooking b/com.lwby.breader.ad/hooking new file mode 100755 index 0000000..bd81966 --- /dev/null +++ b/com.lwby.breader.ad/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.lwby.breader.ad | tee -ai log \ No newline at end of file diff --git a/com.lwby.breader.ad/just_trust_me.js b/com.lwby.breader.ad/just_trust_me.js new file mode 100644 index 0000000..2d56750 --- /dev/null +++ b/com.lwby.breader.ad/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.lwby.breader.ad/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.lwby.breader.ad/just_trust_me_okhttp_hook_finder.js b/com.lwby.breader.ad/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..ce8510f --- /dev/null +++ b/com.lwby.breader.ad/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.lwby.breader.ad/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + continue; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + continue; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + continue; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.lwby.breader.ad/keystore_dump.js b/com.lwby.breader.ad/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.lwby.breader.ad/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.lwby.breader.ad/kill b/com.lwby.breader.ad/kill new file mode 100755 index 0000000..4b2e636 --- /dev/null +++ b/com.lwby.breader.ad/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.lwby.breader.ad \ No newline at end of file diff --git a/com.lwby.breader.ad/object_store.js b/com.lwby.breader.ad/object_store.js new file mode 100644 index 0000000..8cf0283 --- /dev/null +++ b/com.lwby.breader.ad/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.lwby.breader.ad/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.lwby.breader.ad/objection b/com.lwby.breader.ad/objection new file mode 100755 index 0000000..1308b67 --- /dev/null +++ b/com.lwby.breader.ad/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.lwby.breader.ad explore \ No newline at end of file diff --git a/com.lwby.breader.ad/spawn b/com.lwby.breader.ad/spawn new file mode 100755 index 0000000..66d3fd8 --- /dev/null +++ b/com.lwby.breader.ad/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.lwby.breader.ad -l $1 \ No newline at end of file diff --git a/com.lwby.breader.ad/spider.py b/com.lwby.breader.ad/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.lwby.breader.ad/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.lwby.breader.ad/ssl_log.js b/com.lwby.breader.ad/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.lwby.breader.ad/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.lwby.breader.ad/text_view.js b/com.lwby.breader.ad/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.lwby.breader.ad/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.lwby.breader.ad/url.js b/com.lwby.breader.ad/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.lwby.breader.ad/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.lwby.breader.ad/xinitdeploy b/com.lwby.breader.ad/xinitdeploy new file mode 100755 index 0000000..947bcc6 --- /dev/null +++ b/com.lwby.breader.ad/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.lwby.breader.ad') \ No newline at end of file diff --git a/com.meicai.mall/activity_events.js b/com.meicai.mall/activity_events.js new file mode 100644 index 0000000..4d6b6f2 --- /dev/null +++ b/com.meicai.mall/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.meicai.mall/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.meicai.mall/android_ui.js b/com.meicai.mall/android_ui.js new file mode 100644 index 0000000..bdd9826 --- /dev/null +++ b/com.meicai.mall/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.meicai.mall/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.meicai.mall/attach b/com.meicai.mall/attach new file mode 100755 index 0000000..d0d7d29 --- /dev/null +++ b/com.meicai.mall/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.meicai.mall \ No newline at end of file diff --git a/com.meicai.mall/click.js b/com.meicai.mall/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.meicai.mall/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.meicai.mall/edit_text.js b/com.meicai.mall/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.meicai.mall/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.meicai.mall/hook_RN.js b/com.meicai.mall/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.meicai.mall/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.meicai.mall/hooking b/com.meicai.mall/hooking new file mode 100755 index 0000000..e37f49c --- /dev/null +++ b/com.meicai.mall/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.meicai.mall | tee -ai log \ No newline at end of file diff --git a/com.meicai.mall/just_trust_me.js b/com.meicai.mall/just_trust_me.js new file mode 100644 index 0000000..d443434 --- /dev/null +++ b/com.meicai.mall/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.meicai.mall/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.meicai.mall/just_trust_me_okhttp_hook_finder.js b/com.meicai.mall/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..e5f9dd1 --- /dev/null +++ b/com.meicai.mall/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.meicai.mall/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + continue; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + continue; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + continue; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.meicai.mall/keystore_dump.js b/com.meicai.mall/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.meicai.mall/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.meicai.mall/kill b/com.meicai.mall/kill new file mode 100755 index 0000000..bde55cd --- /dev/null +++ b/com.meicai.mall/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.meicai.mall \ No newline at end of file diff --git a/com.meicai.mall/object_store.js b/com.meicai.mall/object_store.js new file mode 100644 index 0000000..3c93b48 --- /dev/null +++ b/com.meicai.mall/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.meicai.mall/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.meicai.mall/objection b/com.meicai.mall/objection new file mode 100755 index 0000000..489162a --- /dev/null +++ b/com.meicai.mall/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.meicai.mall explore \ No newline at end of file diff --git a/com.meicai.mall/spawn b/com.meicai.mall/spawn new file mode 100755 index 0000000..a6e1bcb --- /dev/null +++ b/com.meicai.mall/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.meicai.mall -l $1 \ No newline at end of file diff --git a/com.meicai.mall/spider.py b/com.meicai.mall/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.meicai.mall/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.meicai.mall/ssl_log.js b/com.meicai.mall/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.meicai.mall/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.meicai.mall/text_view.js b/com.meicai.mall/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.meicai.mall/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.meicai.mall/url.js b/com.meicai.mall/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.meicai.mall/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.meicai.mall/xinitdeploy b/com.meicai.mall/xinitdeploy new file mode 100755 index 0000000..a60e8f2 --- /dev/null +++ b/com.meicai.mall/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.meicai.mall') \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/activity_events.js b/com.sankuai.meituan.takeoutnew/activity_events.js new file mode 100644 index 0000000..d607f87 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.sankuai.meituan.takeoutnew/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/android_ui.js b/com.sankuai.meituan.takeoutnew/android_ui.js new file mode 100644 index 0000000..0ad63fc --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.sankuai.meituan.takeoutnew/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/attach b/com.sankuai.meituan.takeoutnew/attach new file mode 100755 index 0000000..922c82a --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.sankuai.meituan.takeoutnew \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/click.js b/com.sankuai.meituan.takeoutnew/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/edit_text.js b/com.sankuai.meituan.takeoutnew/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/hook_RN.js b/com.sankuai.meituan.takeoutnew/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.sankuai.meituan.takeoutnew/hooking b/com.sankuai.meituan.takeoutnew/hooking new file mode 100755 index 0000000..9befb18 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.sankuai.meituan.takeoutnew | tee -ai log \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/just_trust_me.js b/com.sankuai.meituan.takeoutnew/just_trust_me.js new file mode 100644 index 0000000..56e354f --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/just_trust_me.js @@ -0,0 +1,410 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.sankuai.meituan.takeoutnew/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl') was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl') was hooked!"); + }; + } + } + + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.ConscryptEngine) throws java.security.cert.CertificateException was hooked!"); + }; + } + } +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(this, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactory.$new(); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = undefined; + try{ + TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckServerTrusted_8813) { + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckServerTrusted_7015 = undefined; + try{ + TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally { + if (TrustManagerImplCheckServerTrusted_7015) { + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_5587 = undefined; + try{ + TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_5587) { + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_9999 = undefined; + try{ + TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_9999) { + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_2872 = undefined; + try{ + TrustManagerImplCheckTrusted_2872 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', '[B', '[B', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_2872) { + TrustManagerImplCheckTrusted_2872.implementation = function(v0, v1, v2, v3, v4, v5) { + console.log('private java.util.List com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean) throws java.security.cert.CertificateException was hooked!'); + return newArrayList(); + }; + } + } + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/just_trust_me_okhttp_hook_finder.js b/com.sankuai.meituan.takeoutnew/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..cfcd865 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.sankuai.meituan.takeoutnew/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + return; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + return; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + return; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.sankuai.meituan.takeoutnew/keystore_dump.js b/com.sankuai.meituan.takeoutnew/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.sankuai.meituan.takeoutnew/kill b/com.sankuai.meituan.takeoutnew/kill new file mode 100755 index 0000000..6bf2d79 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.sankuai.meituan.takeoutnew \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/object_store.js b/com.sankuai.meituan.takeoutnew/object_store.js new file mode 100644 index 0000000..d447265 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.sankuai.meituan.takeoutnew/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.sankuai.meituan.takeoutnew/objection b/com.sankuai.meituan.takeoutnew/objection new file mode 100755 index 0000000..1a84231 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.sankuai.meituan.takeoutnew explore \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/spawn b/com.sankuai.meituan.takeoutnew/spawn new file mode 100755 index 0000000..9f673e1 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.sankuai.meituan.takeoutnew -l $1 \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/spider.py b/com.sankuai.meituan.takeoutnew/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/ssl_log.js b/com.sankuai.meituan.takeoutnew/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/text_view.js b/com.sankuai.meituan.takeoutnew/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/url.js b/com.sankuai.meituan.takeoutnew/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.sankuai.meituan.takeoutnew/xinitdeploy b/com.sankuai.meituan.takeoutnew/xinitdeploy new file mode 100755 index 0000000..c4e08de --- /dev/null +++ b/com.sankuai.meituan.takeoutnew/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.sankuai.meituan.takeoutnew') \ No newline at end of file diff --git a/com.taobao.taobao/activity_events.js b/com.taobao.taobao/activity_events.js new file mode 100644 index 0000000..a3c8e8a --- /dev/null +++ b/com.taobao.taobao/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.taobao.taobao/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.taobao.taobao/android_ui.js b/com.taobao.taobao/android_ui.js new file mode 100644 index 0000000..a9f977d --- /dev/null +++ b/com.taobao.taobao/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.taobao.taobao/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.taobao.taobao/attach b/com.taobao.taobao/attach new file mode 100755 index 0000000..15fa5fe --- /dev/null +++ b/com.taobao.taobao/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.taobao.taobao \ No newline at end of file diff --git a/com.taobao.taobao/click.js b/com.taobao.taobao/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.taobao.taobao/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.taobao.taobao/edit_text.js b/com.taobao.taobao/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.taobao.taobao/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.taobao.taobao/hook_RN.js b/com.taobao.taobao/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.taobao.taobao/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.taobao.taobao/hooking b/com.taobao.taobao/hooking new file mode 100755 index 0000000..5897974 --- /dev/null +++ b/com.taobao.taobao/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.taobao.taobao | tee -ai log \ No newline at end of file diff --git a/com.taobao.taobao/just_trust_me.js b/com.taobao.taobao/just_trust_me.js new file mode 100644 index 0000000..22bae34 --- /dev/null +++ b/com.taobao.taobao/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.taobao.taobao/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.taobao.taobao/just_trust_me_okhttp_hook_finder.js b/com.taobao.taobao/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..c9d86f9 --- /dev/null +++ b/com.taobao.taobao/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.taobao.taobao/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + continue; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + continue; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + continue; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.taobao.taobao/keystore_dump.js b/com.taobao.taobao/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.taobao.taobao/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.taobao.taobao/kill b/com.taobao.taobao/kill new file mode 100755 index 0000000..3687f4d --- /dev/null +++ b/com.taobao.taobao/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.taobao.taobao \ No newline at end of file diff --git a/com.taobao.taobao/object_store.js b/com.taobao.taobao/object_store.js new file mode 100644 index 0000000..d0caf09 --- /dev/null +++ b/com.taobao.taobao/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.taobao.taobao/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.taobao.taobao/objection b/com.taobao.taobao/objection new file mode 100755 index 0000000..986e6cb --- /dev/null +++ b/com.taobao.taobao/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.taobao.taobao explore \ No newline at end of file diff --git a/com.taobao.taobao/spawn b/com.taobao.taobao/spawn new file mode 100755 index 0000000..e0530f7 --- /dev/null +++ b/com.taobao.taobao/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.taobao.taobao -l $1 \ No newline at end of file diff --git a/com.taobao.taobao/spider.py b/com.taobao.taobao/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.taobao.taobao/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.taobao.taobao/ssl_log.js b/com.taobao.taobao/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.taobao.taobao/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.taobao.taobao/text_view.js b/com.taobao.taobao/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.taobao.taobao/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.taobao.taobao/url.js b/com.taobao.taobao/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.taobao.taobao/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.taobao.taobao/xinitdeploy b/com.taobao.taobao/xinitdeploy new file mode 100755 index 0000000..c31f354 --- /dev/null +++ b/com.taobao.taobao/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.taobao.taobao') \ No newline at end of file diff --git a/com.ttpc.bidding_hall/activity_events.js b/com.ttpc.bidding_hall/activity_events.js new file mode 100644 index 0000000..ee3403b --- /dev/null +++ b/com.ttpc.bidding_hall/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.ttpc.bidding_hall/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/android_ui.js b/com.ttpc.bidding_hall/android_ui.js new file mode 100644 index 0000000..80d444f --- /dev/null +++ b/com.ttpc.bidding_hall/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.ttpc.bidding_hall/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.ttpc.bidding_hall/attach b/com.ttpc.bidding_hall/attach new file mode 100755 index 0000000..cb463c9 --- /dev/null +++ b/com.ttpc.bidding_hall/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.ttpc.bidding_hall \ No newline at end of file diff --git a/com.ttpc.bidding_hall/click.js b/com.ttpc.bidding_hall/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.ttpc.bidding_hall/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/edit_text.js b/com.ttpc.bidding_hall/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.ttpc.bidding_hall/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/hook_RN.js b/com.ttpc.bidding_hall/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.ttpc.bidding_hall/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.ttpc.bidding_hall/hooking b/com.ttpc.bidding_hall/hooking new file mode 100755 index 0000000..a98d5d0 --- /dev/null +++ b/com.ttpc.bidding_hall/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.ttpc.bidding_hall | tee -ai log \ No newline at end of file diff --git a/com.ttpc.bidding_hall/just_trust_me.js b/com.ttpc.bidding_hall/just_trust_me.js new file mode 100644 index 0000000..f908883 --- /dev/null +++ b/com.ttpc.bidding_hall/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.ttpc.bidding_hall/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.ttpc.bidding_hall/just_trust_me_okhttp_hook_finder.js b/com.ttpc.bidding_hall/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..143680e --- /dev/null +++ b/com.ttpc.bidding_hall/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,47 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.ttpc.bidding_hall/radar.dex'); + +var okHostnameVerifier_Verify1_DescribText = "okhttp3.internal.tls.OkHostnameVerifier ---> public boolean verify(String str, SSLSession sSLSession);"; +var okHostnameVerifier_Verify2_DescribText = "okhttp3.internal.tls.OkHostnameVerifier ---> public boolean verify(String str, X509Certificate x509Certificate);"; +var certificatePinner_Check_DescribText = "okhttp3.CertificatePinner ---> public void check(String str, List list);"; + +Java.perform(function () { + console.log("开始模糊匹配okhttp的混淆类......"); + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + var foundCount = 0; + Java.enumerateLoadedClasses({ + onMatch: function (className) { + var matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(className); + if (matchOk3CertificatePinnerCheckResult && matchOk3CertificatePinnerCheckResult.length == 2) { + let describText = certificatePinner_Check_DescribText + " matching: " + matchOk3CertificatePinnerCheckResult[0] + "---> public void " + matchOk3CertificatePinnerCheckResult[1] + "(String str, List list)"; + console.log(describText+"\n"); + foundCount += 1; + } + + var matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(className); + if (matchOk3OkHostnameVerifierVerifyResult && matchOk3OkHostnameVerifierVerifyResult.length == 3) { + let describText1 = okHostnameVerifier_Verify1_DescribText + " matching: " + matchOk3OkHostnameVerifierVerifyResult[0] + "---> public boolean " + matchOk3OkHostnameVerifierVerifyResult[1] + "(String str, SSLSession sSLSession)"; + let describText2 = okHostnameVerifier_Verify2_DescribText + " matching: " + matchOk3OkHostnameVerifierVerifyResult[0] + "---> public boolean " + matchOk3OkHostnameVerifierVerifyResult[2] + "(String str, X509Certificate x509Certificate)"; + console.log(describText1+"\n"); + console.log(describText2+"\n"); + foundCount += 2; + } + + }, + onComplete: function () { + if (foundCount > 0) { + console.log("模糊匹配完成,请打开jadx确认以上结果是否匹配正确。如果正确,你可以把正确的hook点修改到just_trust_me.js的processOkHttp方法里。祝你抓包成功!"); + }else{ + console.log("模糊匹配完成,未找到任何结果!"); + } + } + }); +}); + + diff --git a/com.ttpc.bidding_hall/keystore_dump.js b/com.ttpc.bidding_hall/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.ttpc.bidding_hall/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.ttpc.bidding_hall/kill b/com.ttpc.bidding_hall/kill new file mode 100755 index 0000000..58419e3 --- /dev/null +++ b/com.ttpc.bidding_hall/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.ttpc.bidding_hall \ No newline at end of file diff --git a/com.ttpc.bidding_hall/object_store.js b/com.ttpc.bidding_hall/object_store.js new file mode 100644 index 0000000..7b21450 --- /dev/null +++ b/com.ttpc.bidding_hall/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.ttpc.bidding_hall/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.ttpc.bidding_hall/objection b/com.ttpc.bidding_hall/objection new file mode 100755 index 0000000..ac0ce49 --- /dev/null +++ b/com.ttpc.bidding_hall/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.ttpc.bidding_hall explore \ No newline at end of file diff --git a/com.ttpc.bidding_hall/spawn b/com.ttpc.bidding_hall/spawn new file mode 100755 index 0000000..682aa79 --- /dev/null +++ b/com.ttpc.bidding_hall/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.ttpc.bidding_hall -l $1 \ No newline at end of file diff --git a/com.ttpc.bidding_hall/spider.py b/com.ttpc.bidding_hall/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.ttpc.bidding_hall/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.ttpc.bidding_hall/ssl_log.js b/com.ttpc.bidding_hall/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.ttpc.bidding_hall/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/text_view.js b/com.ttpc.bidding_hall/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.ttpc.bidding_hall/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/url.js b/com.ttpc.bidding_hall/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.ttpc.bidding_hall/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.ttpc.bidding_hall/xinitdeploy b/com.ttpc.bidding_hall/xinitdeploy new file mode 100755 index 0000000..f64ba1f --- /dev/null +++ b/com.ttpc.bidding_hall/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.ttpc.bidding_hall') \ No newline at end of file diff --git a/com.twitter.android/activity_events.js b/com.twitter.android/activity_events.js new file mode 100644 index 0000000..f9c1616 --- /dev/null +++ b/com.twitter.android/activity_events.js @@ -0,0 +1,170 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(),javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +loadDexfile('/data/user/0/com.twitter.android/radar.dex'); + +Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper'); + var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle'); + android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent'); + android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1); + printBeat(beat); + }; + var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle'); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v1)); + var executor = this.hashCode(); + var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)'; + var beat = newMethodBeat(beatText, executor); + android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2); + printBeat(beat); + }; + var android_app_Activity_clz = Java.use('android.app.Activity'); + var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle'); + android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) { + log("Intent>>>>>>>"+radarAndroidClz.getIntentProfile(v0)); + log("Flags>>>>>>>"+v1); + log("Bundle>>>>>>>"+radarAndroidClz.getBundleProfile(v2)); + var executor = this.hashCode(); + var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)'; + var beat = newMethodBeat(beatText, executor); + android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2); + printBeat(beat); + }; +}); \ No newline at end of file diff --git a/com.twitter.android/android_ui.js b/com.twitter.android/android_ui.js new file mode 100644 index 0000000..36f2be2 --- /dev/null +++ b/com.twitter.android/android_ui.js @@ -0,0 +1,194 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; +loadDexfile('/data/user/0/com.twitter.android/radar.dex'); +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/" + str2 + "/")); + if (str1 && str1.match && str1.match(reg)) { + return true; + } else { + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function log(str) { + console.log(str); +}; + +//虽然我们习惯用fastjson一行将对象转成json字符串,但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来,很多人不知道。在frida中所有代码都是透明的,你随便调...... +function toJson(javaObject) { + var gsonClz = Java.use("com.google.gson.Gson"); + var toJsonMethod = gsonClz.toJson.overload("java.lang.Object"); + return toJsonMethod.call(gsonClz.$new(), javaObject); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function findViewById(viewId) { + var report = ""; + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var viewInfo = radarAndroidClz.getViewInfo(viewId + ""); + if (!viewInfo) { + report += "Not Found View." + return; + } + report += ("------------------View--------------------") + "\n"; + report += ("View Id: " + viewInfo.getViewId()) + "\n"; + report += ("View IdName: " + viewInfo.getViewIdName()) + "\n"; + report += ("View Class: " + viewInfo.getName()) + "\n"; + report += ("View SuperClass: " + viewInfo.getSuperClazz()) + "\n"; + report += ("View ImplementInterfaces: " + viewInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = viewInfo.getAndroidApkFields(); + report += ("View Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = viewInfo.methods(); + report += ("View Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); + log(report); +} + +function startActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.startActivity(activityName); + }); +} + +function contextStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivity(activityName); + }); +} + +function contextStartActivityForNewTask(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.contextStartActivityForNewTask(activityName); + }); +} + +function topActivityStartActivity(activityName) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.topActivityStartActivity(activityName); + }); +} + +function home() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.home(); + }); +} + +function back() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.back(); + }); +} + +function finishCurrentActivity() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + androidUIClz.finishCurrentActivity(); + }); +} + +function clickByText(text) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickByText(text)); + }); +} + +function clickById(id) { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.clickById(id)); + }); +} + +function hover(x,y,upStepLength) { + Java.perform(function() { + var androidui = Java.use("gz.radar.AndroidUI"); + androidui.hover(x,y,upStepLength); + }); +} + +function viewTree() { + Java.perform(function() { + var androidUIClz = Java.use("gz.radar.AndroidUI"); + log(androidUIClz.viewTree()); + }); +} \ No newline at end of file diff --git a/com.twitter.android/attach b/com.twitter.android/attach new file mode 100755 index 0000000..81750f6 --- /dev/null +++ b/com.twitter.android/attach @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida $HOOKER_DRIVER -l $1 com.twitter.android \ No newline at end of file diff --git a/com.twitter.android/click.js b/com.twitter.android/click.js new file mode 100644 index 0000000..501045c --- /dev/null +++ b/com.twitter.android/click.js @@ -0,0 +1,49 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +Java.perform(function() { + var textViewClz = Java.use("android.widget.TextView"); + var android_view_View_clz = Java.use('android.view.View'); + var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload(); + android_view_View_clz_method_performClick_u6ef.implementation = function() { + var invokeId = Math.random().toString(36).slice( - 8); + var startTime = new Date().getTime(); + var executor = 'obj:' + this.hashCode(); + var ret = android_view_View_clz_method_performClick_u6ef.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + //console.log("ViewText: " + Java.cast(this, textViewClz).getText()); + console.log("ViewClz: " + clz); + console.log("ViewId: " + viewId); + methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor); + return ret; + }; +}); \ No newline at end of file diff --git a/com.twitter.android/edit_text.js b/com.twitter.android/edit_text.js new file mode 100644 index 0000000..ec2c5ba --- /dev/null +++ b/com.twitter.android/edit_text.js @@ -0,0 +1,97 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function makeClass(className) { + var classClz = Java.use("java.lang.Class"); + var forNameFunc = classClz.forName.overload("java.lang.String"); + return forNameFunc.call(classClz, className); +}; + +function isClass(obj, superClzName) { + var objClz = obj.getClass(); + var superClz = makeClass(superClzName); + return superClz.isAssignableFrom(objClz); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + var charSequenceClz = Java.use("java.lang.CharSequence"); + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + setTextFunc.call(this, v0); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("text: " + v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor); + } + }; + } + //EditText的getText继承自TextView + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = getTextFunc.call(this); + if (isClass(this, "android.widget.EditText")) { + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor); + } + return editable; + }; + } + //AppCompatEditText 有自定义的getText所以单独hook + if (classExists("androidx.appcompat.widget.AppCompatEditText")) { + var appCompatEditTextClz = Java.use("androidx.appcompat.widget.AppCompatEditText"); + var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload(); + appCompatEditTextClzGetTextFunc.implementation = function() { + var startTime = new Date().getTime(); + var editable = appCompatEditTextClzGetTextFunc.call(this); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("EditTextClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + Java.cast(editable, charSequenceClz)); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.twitter.android/hook_RN.js b/com.twitter.android/hook_RN.js new file mode 100644 index 0000000..de44671 --- /dev/null +++ b/com.twitter.android/hook_RN.js @@ -0,0 +1,46 @@ + +function hook_RegisterNatives() { + var symbols = Module.enumerateSymbolsSync("libart.so"); + var addrRegisterNatives = null; + for (var i = 0; i < symbols.length; i++) { + var symbol = symbols[i]; + + //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi + if (symbol.name.indexOf("art") >= 0 && + symbol.name.indexOf("JNI") >= 0 && + symbol.name.indexOf("RegisterNatives") >= 0 && + symbol.name.indexOf("CheckJNI") < 0) { + addrRegisterNatives = symbol.address; + console.log("RegisterNatives is at ", symbol.address, symbol.name); + } + } + + if (addrRegisterNatives != null) { + Interceptor.attach(addrRegisterNatives, { + onEnter: function (args) { + console.log("[RegisterNatives] method_count:", args[3]); + var env = args[0]; + var java_class = args[1]; + var class_name = Java.vm.tryGetEnv().getClassName(java_class); + //console.log(class_name); + + var methods_ptr = ptr(args[2]); + + var method_count = parseInt(args[3]); + for (var i = 0; i < method_count; i++) { + var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); + var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); + var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); + + var name = Memory.readCString(name_ptr); + var sig = Memory.readCString(sig_ptr); + var find_module = Process.findModuleByAddress(fnPtr_ptr); + console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); + + } + } + }); + } +} + +setImmediate(hook_RegisterNatives); diff --git a/com.twitter.android/hooking b/com.twitter.android/hooking new file mode 100755 index 0000000..ae4ad22 --- /dev/null +++ b/com.twitter.android/hooking @@ -0,0 +1,5 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +echo "hooking $1" > log +date | tee -ai log +frida $HOOKER_DRIVER -l $1 com.twitter.android | tee -ai log \ No newline at end of file diff --git a/com.twitter.android/just_trust_me.js b/com.twitter.android/just_trust_me.js new file mode 100644 index 0000000..8a336cb --- /dev/null +++ b/com.twitter.android/just_trust_me.js @@ -0,0 +1,318 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.twitter.android/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + }; +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactoryEmptyConstructor.call(this); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/com.twitter.android/just_trust_me_okhttp_hook_finder.js b/com.twitter.android/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..4eaf922 --- /dev/null +++ b/com.twitter.android/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.twitter.android/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + return; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + return; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + return; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/com.twitter.android/keystore_dump.js b/com.twitter.android/keystore_dump.js new file mode 100644 index 0000000..d58589b --- /dev/null +++ b/com.twitter.android/keystore_dump.js @@ -0,0 +1,110 @@ +//在https双向认证的情况下,dump客户端证书为p12. 证书密码: hooker +var password = "hooker"; + + + +function dateFormat(fmt, date) { + let ret; + const opt = { + "Y+": date.getFullYear().toString(), + // 年 + "m+": (date.getMonth() + 1).toString(), + // 月 + "d+": date.getDate().toString(), + // 日 + "H+": date.getHours().toString(), + // 时 + "M+": date.getMinutes().toString(), + // 分 + "S+": date.getSeconds().toString() // 秒 + }; + for (let k in opt) { + ret = new RegExp("(" + k + ")").exec(fmt); + if (ret) { + fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) + }; + }; + return fmt; +} + +function random(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function getNowTime() { + return dateFormat("YYYY_mm_dd_HH_MM_SS", new Date()) + "_" + random(1, 100); +} + +function getPackageName() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context.getPackageName(); +}; + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +function dump2sdcard(pri, p7, filePath) { + console.log("dump:" + filePath); + var X509CertificateClass = Java.use("java.security.cert.X509Certificate"); + var myX509 = Java.cast(p7, X509CertificateClass); + var chain = Java.array("java.security.cert.X509Certificate", [myX509]); + var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); + ks.load(null, null); + ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain); + try { + var out = Java.use("java.io.FileOutputStream").$new(filePath); + ks.store(out, Java.use('java.lang.String').$new(password).toCharArray()); + } catch(error) { + console.log(error); + } +} + + + +Java.perform(function() { + var packageName = getPackageName(); + console.log("在https双向认证的情况下,dump客户端证书为p12. 存储位置:/data/user/0/"+packageName+"/client_keystore_{nowtime}.p12 证书密码: hooker"); + Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getPrivateKey(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + "_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + printBeat(beat); + return result; + } + Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function() { + var executor = this.hashCode(); + var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()'; + var beat = newMethodBeat(beatText, executor); + var result = this.getCertificateChain(); + let filePath = '/data/user/0/' + packageName + "/client_keystore_" + getNowTime() + '.p12'; + dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath); + return result; + } +}) diff --git a/com.twitter.android/kill b/com.twitter.android/kill new file mode 100755 index 0000000..01e9c30 --- /dev/null +++ b/com.twitter.android/kill @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +frida-kill $HOOKER_DRIVER com.twitter.android \ No newline at end of file diff --git a/com.twitter.android/note.txt b/com.twitter.android/note.txt new file mode 100644 index 0000000..64a0a4a --- /dev/null +++ b/com.twitter.android/note.txt @@ -0,0 +1,2 @@ +微博列表 +curl -H 'Host: api.twitter.com' -H 'Cookie: personalization_id=v1_FWdmcZa+8Up45cYYMMvYNA==; guest_id=v1%3A162165414232371552' -H 'cache-control: no-store' -H 'x-b3-traceid: 561c88cb00b06db6' -H 'os-security-patch-level: 2019-03-01' -H 'x-twitter-client-flavor: ' -H 'kdt: Da7pB1RLHl5LKtcUR94iYfuwIB5tGAd493iOETOZ' -H 'user-agent: TwitterAndroid/8.84.0-release.00 (28840000-r-0) MI+MAX+2/7.1.1 (Xiaomi;MI+MAX+2;Xiaomi;oxygen;0;;1;2015)' -H 'timezone: Asia/Shanghai' -H 'x-twitter-client: TwitterAndroid' -H 'x-twitter-client-language: zh-CN' -H 'x-twitter-client-deviceid: e341376c6094e143' -H 'x-twitter-api-version: 5' -H 'x-twitter-utcoffset: +0800' -H 'optimize-body: true' -H 'x-twitter-active-user: yes' -H 'x-twitter-client-version: 8.84.0-release.00' -H 'x-client-uuid: dc23e5e4-b9ee-4557-a182-19a8e28c785c' -H 'accept: application/json' -H 'authorization: OAuth realm="http://api.twitter.com/", oauth_version="1.0", oauth_token="1388074025453121538-5g97mHGhtBpmAbBQvmYGWyUm9eFlKc", oauth_nonce="2047162407856074744124630325634597", oauth_timestamp="1621654380", oauth_signature="iyhZ8%2FfSYWEaFxA1%2F0Jbh11Yh08%3D", oauth_consumer_key="3nVuSoBZnx6U4vzUxf5w", oauth_signature_method="HMAC-SHA1"' -H 'accept-language: zh-CN' --compressed 'https://api.twitter.com/2/timeline/profile/99008565.json?cursor=HBaCwKbpgdXVpyUAAA%3D%3D&pc=false&include_tweet_replies=false&earned=true&count=40&autoplay_enabled=true&include_entities=true&include_cards=true&cards_platform=Android-12&include_carousels=true&ext=mediaRestrictions%2CaltText%2CmediaStats%2CmediaColor%2Cinfo360%2ChighlightedLabel&include_media_features=true&include_blocking=true&include_blocked_by=true&include_quote_count=true&include_ext_media_availability=true&simple_quoted_tweet=true&tweet_mode=extended&include_reply_count=true&include_composer_source=true&include_user_entities=true&include_profile_interstitial_type=true' diff --git a/com.twitter.android/object_store.js b/com.twitter.android/object_store.js new file mode 100644 index 0000000..e7a80a1 --- /dev/null +++ b/com.twitter.android/object_store.js @@ -0,0 +1,61 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + }); +}; + +loadDexfile('/data/user/0/com.twitter.android/radar.dex'); + + +function log(str) { + console.log(str); +}; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while (new Date().getTime() < startTime) {} +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function object2Json(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(fastTojson(obj)); + }else{ + log("Not found this object."); + } +} + +function object2String(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + if (obj) { + log(obj.toString()); + }else{ + log("Not found this object."); + } +} + +function getObject(objectId) { + var radarPropertiesClz = Java.use("gz.radar.objects.ObjectsStore"); + var obj = radarPropertiesClz.getObject(objectId+""); + return obj; +} + + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + diff --git a/com.twitter.android/objection b/com.twitter.android/objection new file mode 100755 index 0000000..e246b4a --- /dev/null +++ b/com.twitter.android/objection @@ -0,0 +1,3 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) +objection -d -g com.twitter.android explore \ No newline at end of file diff --git a/com.twitter.android/spawn b/com.twitter.android/spawn new file mode 100755 index 0000000..b5cfc4a --- /dev/null +++ b/com.twitter.android/spawn @@ -0,0 +1,4 @@ +#!/bin/bash +HOOKER_DRIVER=$(cat ../.hooker_driver) + +frida $HOOKER_DRIVER --no-pause -f com.twitter.android -l $1 \ No newline at end of file diff --git a/com.twitter.android/spider.py b/com.twitter.android/spider.py new file mode 100644 index 0000000..77ef6bb --- /dev/null +++ b/com.twitter.android/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/com.twitter.android/ssl_log.js b/com.twitter.android/ssl_log.js new file mode 100644 index 0000000..69eff81 --- /dev/null +++ b/com.twitter.android/ssl_log.js @@ -0,0 +1,21 @@ +function startTLSKeyLogger() { + + const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new'); + const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'); + + + const keyLogCallback = new NativeCallback(function (ssl, line) { + console.log(new NativePointer(line).readCString()); + }, 'void', ['pointer', 'pointer']); + + const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']); + + Interceptor.attach(SSL_CTX_new_addr, { + onLeave: function(retval) { + const ssl = new NativePointer(retval); + SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); + } + }); +} + +startTLSKeyLogger(); \ No newline at end of file diff --git a/com.twitter.android/text_view.js b/com.twitter.android/text_view.js new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/com.twitter.android/text_view.js @@ -0,0 +1,50 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var textViewClz = Java.use("android.widget.TextView"); + + if (textViewClz.setText) { + var setTextFunc = textViewClz.setText.overload("java.lang.CharSequence"); + setTextFunc.implementation = function(v0) { + var startTime = new Date().getTime(); + var clz = this.getClass().getName(); + var viewId = this.getId(); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text:" + v0); + setTextFunc.call(this, v0); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.setText()", executor); + }; + } + if (textViewClz.getText) { + var getTextFunc = textViewClz.getText.overload(); + getTextFunc.implementation = function() { + var clz = this.getClass().getName(); + var viewId = this.getId(); + var editable = getTextFunc.call(this); + console.log("TextViewClz: " + clz); + console.log("ViewId: " + viewId); + console.log("Text: " + editable.toString()); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "android.widget.TextView.getText()", executor); + return editable; + }; + } +}); \ No newline at end of file diff --git a/com.twitter.android/url.js b/com.twitter.android/url.js new file mode 100644 index 0000000..ecd954b --- /dev/null +++ b/com.twitter.android/url.js @@ -0,0 +1,132 @@ +function tryGetClass(className) { + var clz = undefined; + try { + clz = Java.use(className); + } catch(e) {} + return clz; +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------startFlag:" + beat.invokeId + ",objectHash:" + beat.executor + ",thread(id:" + beat.threadId + ",name:" + beat.threadName + "),timestamp:" + beat.startTime + "---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) + "---------------\n"); + console.log(str); +}; + +var containRegExps = new Array() + +var notContainRegExps = new Array(RegExp(/\.jpg/), RegExp(/\.png/)) + +function check(str) { + str = str.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var uriParseClz = Java.use('java.net.URI'); + var uriParseClzConstruct = uriParseClz.$init.overload("java.lang.String"); + uriParseClzConstruct.implementation = function(url) { + var result = uriParseClzConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URI(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + // URL + var URLClz = Java.use('java.net.URL'); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var urlConstruct = URLClz.$init.overload("java.lang.String"); + urlConstruct.implementation = function(url) { + var result = urlConstruct.call(this, url); + var executor = this.hashCode(); + var beatText = "url:" + url + "\npublic java.net.URL(String)"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return result; + }; + + //ok系统原生支持 + var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder'); + if (sysBuilderClz) { + sysBuilderClz.build.implementation = function() { + var okRequestResult = this.build(); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\ncom.android.okhttp.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + //ok本地依赖 + var builderClz = tryGetClass('okhttp3.Request$Builder'); + if (builderClz) { + var buildFunc = builderClz.build.overload(); + buildFunc.implementation = function() { + var okRequestResult = buildFunc.call(this); + var httpUrl = okRequestResult.url(); + var url = httpUrl.toString(); + var executor = this.hashCode(); + var beatText = "url:" + url + "\nokhttp3.Request.Builder.build()"; + var beat = newMethodBeat(beatText, executor); + if (check(url)) { + printBeat(beat); + } + return okRequestResult + }; + } + + var android_net_Uri_clz = Java.use('android.net.Uri'); + var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String'); + android_net_Uri_clz_method_parse_u5rj.implementation = function(url) { + var executor = 'Class'; + var beatText = url + '\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)'; + var beat = newMethodBeat(beatText, executor); + var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url); + if (check(url)) { + printBeat(beat); + } + return ret; + }; +}); \ No newline at end of file diff --git a/com.twitter.android/xinitdeploy b/com.twitter.android/xinitdeploy new file mode 100755 index 0000000..43b9c81 --- /dev/null +++ b/com.twitter.android/xinitdeploy @@ -0,0 +1,175 @@ +#! /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 + +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) + +xinitDeploy('com.twitter.android') \ No newline at end of file diff --git a/disable_sslpinning.py b/disable_sslpinning.py new file mode 100755 index 0000000..6a4e8cd --- /dev/null +++ b/disable_sslpinning.py @@ -0,0 +1,11 @@ +from objection.state.connection import state_connection +from objection.utils.agent import Agent + + +state_connection.gadget_name = "{appPackageName}" +state_connection.agent = Agent() +state_connection.agent.inject() +api = state_connection.get_api() +api.android_ssl_pinning_disable(False) + +inputCommand = input("input any word to quit!!!") diff --git a/hooker b/hooker new file mode 100755 index 0000000..784ae81 --- /dev/null +++ b/hooker @@ -0,0 +1,45 @@ +#!/bin/bash + +HOOKER_DRIVER=$(cat .hooker_driver) +while : +do + frida-ps $HOOKER_DRIVER -ai | grep -v '@' | grep -v ' - ' + echo "Enter the need to attach package." + read -p ": " package + if [[ $package = ex ]] || [[ $package = exit ]] || [[ $package = quit ]] || [[ $package = q ]]; then + break + fi + echo "It's $package that you have attached app." + python3 hooker.py -p $package -g true + echo "--------------------------------------------------" + while : + do + echo "Please enter e, s, j, c or ex command." + echo "a: Discovering activities." + echo "b: Discovering services." + echo "c: Discovering object. eg:'c {objectId}'" + echo "d: Object2Explain. eg:'d {objectId}'" + echo "v: Discovering view. eg:'v {viewId}'" + echo "e: Determines whether a class exists. eg:'e android.app.Application'" + echo "s: Discovering classes by a class'regex. eg:'s com.tencent.mm.Message.*'" + echo "t: Discovering offspring classes by a class'name. eg:'t com.tencent.mm.BasicActivity'" + echo "j: Generating hooked js. eg:'j okhttp3.Request\$Builder:build'" + echo "k: Generating hooked the string generation js with a keyword. eg:'k {YourKeyword}'" + echo "l: Generating hooked the param generation js with a param keyword. eg:'l {YourKeyword}'" + echo "m: Discovering so module." + echo "ex: Exit to the upper layer. eg:'ex'" + read -p ": " op + if [[ $op = ex ]] || [[ $op = exit ]]; then + break + fi + if [[ $op = a ]] || [[ $op = b ]]; then + python3 hooker.py -p $package -$op true + continue + fi + if [[ $op =~ ^v ]] || [[ $op =~ ^l ]] || [[ $op =~ ^c ]] || [[ $op =~ ^d ]] || [[ $op =~ ^s ]] || [[ $op =~ ^t ]] || [[ $op =~ ^e ]] || [[ $op =~ ^j ]] || [[ $op =~ ^k ]] || [[ $op =~ ^m ]] ;then + python3 hooker.py -p $package -$op + continue + fi + echo "Wronging command!" + done +done \ No newline at end of file diff --git a/hooker.py b/hooker.py old mode 100755 new mode 100644 index 263c7dc..82cb6f4 --- a/hooker.py +++ b/hooker.py @@ -1,874 +1,146 @@ -#!/usr/bin/env python3 - ''' Created on 2020年3月23日 @author: stephen ''' - -default_frida_server_arm = "frida-server-16.7.19-android-arm" -default_frida_server_arm64 = "frida-server-16.7.19-android-arm64" - - import frida, sys import os import io import re -import stat -import time -import json import getopt import traceback +import run_env import base64 import time +import colorful import platform -import threading -import adbutils -import hashlib -import shutil -import textwrap -import zipfile -import queue -import sqlite3 -import itertools -import jsbeautifier -import logging -import subprocess -import filecmp -import argparse -import pprint -import random -import signal -import socket -import struct -import binascii -from git import Repo -from pathlib import Path -from loguru import logger -from datetime import datetime -from collections import Counter -from dataclasses import dataclass -from androguard.core import androconf -from androguard.core.bytecodes import apk -from androguard.core.bytecodes import dvm -from androguard.core.analysis.analysis import MethodAnalysis - -from typing import Optional, Tuple, List, Dict -from adbutils.errors import AdbError -from prompt_toolkit import PromptSession -from prompt_toolkit.completion import Completer, Completion -from prompt_toolkit.completion import WordCompleter -from prompt_toolkit.completion import NestedCompleter -from prompt_toolkit.patch_stdout import patch_stdout -from wcwidth import wcswidth - - -def find_android_home() -> Optional[str]: - """ - 在 macOS/Linux 上查找 Android SDK 路径 - - Returns: - Android SDK 路径,如果未找到则返回 None - """ - # 1. 首先检查环境变量 - for env_var in ['ANDROID_HOME', 'ANDROID_SDK_ROOT']: - sdk_path = os.environ.get(env_var) - if sdk_path and os.path.isdir(sdk_path): - return os.path.abspath(sdk_path) - - # 2. 检查操作系统特定的默认安装路径 - if sys.platform == 'darwin': # macOS - default_paths = [ - os.path.join(os.path.expanduser('~'), 'Library', 'Android', 'sdk'), - '/usr/local/share/android-sdk', - '/opt/homebrew/share/android-sdk', - '/opt/android-sdk', - '/usr/local/opt/android-sdk', - ] - else: # Linux - default_paths = [ - os.path.join(os.path.expanduser('~'), 'Android', 'Sdk'), - '/usr/lib/android-sdk', - '/opt/android-sdk', - '/usr/local/android-sdk', - '/opt/local/android-sdk', - ] - - # 检查默认路径 - for path in default_paths: - if path and os.path.isdir(path): - # 验证是否是有效的 Android SDK 目录 - if os.path.exists(os.path.join(path, 'build-tools')): - return os.path.abspath(path) - - return None - - -def get_oldest_dx_d8_path(android_home: Optional[str] = None, min_version: str = "30.0.0") -> Tuple[ - Optional[str], Optional[str]]: - """ - 查找最老的 dx 和最老的 d8 编译器路径,但版本号不低于 min_version +from run_env import xinitPyScript - Args: - android_home: Android SDK 路径,如果为 None 则自动查找 - min_version: 最小版本号,默认 30.0.0 - - Returns: - (最老的d8文件路径, 最老的dx文件路径),如果未找到则返回 (None, None) - """ - # 1. 获取 Android SDK 路径 - if android_home is None: - android_home = find_android_home() - - if not android_home: - return None, None - - # 2. 检查 build-tools 目录 - build_tools_dir = os.path.join(android_home, 'build-tools') - if not os.path.isdir(build_tools_dir): - return None, None - - # 3. 解析最小版本号 - def parse_version(version_str: str) -> List[int]: - """将版本号字符串转换为整数列表便于比较""" - parts = version_str.split('.') - result = [] - for part in parts: - try: - result.append(int(part)) - except ValueError: - # 如果包含非数字部分,只取数字部分 - num_part = re.search(r'^\d+', part) - if num_part: - result.append(int(num_part.group())) - else: - result.append(0) - # 补齐到3位 - while len(result) < 3: - result.append(0) - return result - - min_version_parts = parse_version(min_version) - - # 4. 遍历所有 build-tools 版本,分别收集满足条件的 dx 和 d8 文件 - d8_files = [] - dx_files = [] - - # 获取所有版本目录 - for item in os.listdir(build_tools_dir): - version_dir = os.path.join(build_tools_dir, item) - if not os.path.isdir(version_dir): - continue - - # 检查是否是有效的版本号格式(如 34.0.0) - if not re.match(r'^\d+(\.\d+)*$', item): - continue - - # 检查版本号是否 >= 30.0.0 - version_parts = parse_version(item) - - # 版本比较函数 - def version_gte(v1, v2): - """比较版本 v1 是否大于等于 v2""" - for part1, part2 in zip(v1, v2): - if part1 > part2: - return True - elif part1 < part2: - return False - return True # 完全相等 - - # 如果版本号小于最小版本,跳过 - if not version_gte(version_parts, min_version_parts): - continue - # 查找 dx 和 d8 文件 - dx_path = os.path.join(version_dir, 'dx') - d8_path = os.path.join(version_dir, 'd8') +warn = colorful.red +info = colorful.yellow - # 分别收集 d8 和 dx 文件 - if os.path.isfile(d8_path) and os.access(d8_path, os.X_OK): - d8_files.append((item, d8_path)) +def readRadarDexfile(): + f = open("radar.dex","rb") + data = f.read() + f.close() + return data - if os.path.isfile(dx_path) and os.access(dx_path, os.X_OK): - dx_files.append((item, dx_path)) - - # 5. 获取最老的 d8 和 dx(在>=30.0.0的版本中) - oldest_d8_path = None - oldest_dx_path = None - - if d8_files: - # 按版本号升序排列(最老的在前) - d8_files.sort(key=lambda x: parse_version(x[0])) - oldest_d8_path = d8_files[0][1] - - if dx_files: - # 按版本号升序排列(最老的在前) - dx_files.sort(key=lambda x: parse_version(x[0])) - oldest_dx_path = dx_files[0][1] - - return oldest_d8_path, oldest_dx_path - - -def withColor(string, fg, bg=49): - print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) -#front color -Red = 1 -Green = 2 -Yellow = 3 -Blue = 4 -Magenta = 5 -Cyan = 6 -White = 7 - -def red(string): - return withColor(string, Red+30) # Red -def green(string): - return withColor(string, Green+30) # Green -def yellow(string): - return withColor(string, Yellow+30) # Yellow -def blue(string): - return withColor(string, Blue+30) # Blue -def magenta(string): - return withColor(string, Magenta+30) # Magenta -def cyan(string): - return withColor(string, Cyan+30) # Cyan -def white(string): - return withColor(string, White+30) # White -#日志打印颜色定义 -warn = red -info = yellow - -def print_js_file(filenames :list): - if not filenames: +def checkRadarDex(packageName, online_script): + radarClass = "gz.radar.ClassRadar" + if online_script.exports.containsclass(radarClass): return - GREEN = "\033[32m" - RESET = "\033[0m" - columns, _ = shutil.get_terminal_size() - # 计算每个字段宽度 - max_len = max(len(name) for name in filenames) + 2 - items_per_line = max(1, columns // max_len) - # 输出带颜色的文件名 - for i in range(0, len(filenames), items_per_line): - line = "".join(f"{GREEN}{name.ljust(max_len)}{RESET}" for name in filenames[i:i + items_per_line]) - print(line) - -def read_js_resource(filename): - return io.open('./js/' + filename,'r',encoding= 'utf8').read() - -cmd_session = None - - -adb_device = None - -def _init_adb_device(): - global adb_device - adb_device = adbutils.adb.device() - -_init_adb_device() - -def _shell(cmd, stream=False): - return adb_device.shell(cmd, stream=stream) - -def run_su_command(cmd, not_read=False): - try: - if not_read: - _shell(f"{cmd} > /dev/null 2>&1 &") - time.sleep(1) - return - output = _shell(cmd).strip() - if output: - return output - except Exception: - pass - conn = _shell(["su", "-c", cmd], stream=True) - try: - if not_read: - time.sleep(1) - return - output = conn.read_until_close() - return output.strip() - finally: - try: - conn.close() - except Exception as e: - pass - - -def get_is_magisk_root() -> bool: - out = run_su_command("su -c id") - if "uid=0" not in out: - return False - # 有些设备不一定带 context 字段,但你这台带了,带了就几乎 100% 是 Magisk - if "context=u:r:magisk:s0" in out: - return True - # 没有 context 也可能是 root(或别的 su),再加个 Magisk 文件特征判断更保险 - magisk_marker = run_su_command("ls /data/adb/magisk.db 2>/dev/null") - return bool(magisk_marker) - -is_magisk_root = get_is_magisk_root() + data = readRadarDexfile() + radarDexPath = '/data/user/0/'+packageName+'/radar.dex'; + if not online_script.exports.checkfile(radarDexPath, len(data)): + radarDexBase64 = base64.b64encode(data).decode() + #info("update radar.dex......"+str(len(radarDexBase64))) + online_script.exports.write(radarDexPath, radarDexBase64) + time.sleep(1) + online_script.exports.loaddex('gz.radar.ClassRadar', radarDexPath) + if not online_script.exports.containsclass(radarClass): + warn("injecting radar.dex failure.") +def on_message(message, data): + pass -#初始化frida运行环境 -def is_frida_working_via_attach(target_package="com.android.systemui"): +def is_number(s): try: - __device = frida.get_usb_device(timeout=3) # or use add_remote_device(ip) - pid = __device.get_process(target_package).pid # 先确认包是否存在 - _session = __device.attach(pid) - _session.detach() + int(s) return True - except frida.ServerNotRunningError: - #info("其他异常: ServerNotRunningError") - return False - except frida.ProcessNotFoundError: - #info("⚠️ 找不到进程,说明包名可能错误,但 frida 正常") - return True # 仍然说明 frida-server 已连通 - except frida.TimedOutError: - #info("❌ 连接超时,frida-server 可能未运行或设备未连接") - return False - except Exception as e: - #info(f"其他异常: {e}") - return False - -def check_remote_file_exists(path): - result = adb_device.shell(f"test -f {path} && echo exists || echo missing") - return result.strip() == "exists" - -def check_remote_dir_exists(path): - result = adb_device.shell(f"[ -d {path} ] && echo exists || echo not_exists") - return result.strip() == "exists" - -def get_cpu_arch(): - abi = adb_device.shell("getprop ro.product.cpu.abi").strip() - if "arm64" in abi: - return "arm64" - elif "armeabi" in abi: - return "arm" - elif "x86_64" in abi: - return "x86_64" - elif "x86" in abi: - return "x86" - return "arm64" - -def choose_frida_server(): - cpu_arch = get_cpu_arch() - if "arm64" == cpu_arch: - return default_frida_server_arm64 - elif "arm" == cpu_arch: - return default_frida_server_arm - info("For simulator, please start frida-server manually first. Thank you") - sys.exit(2) - -def pull_file_to_local(remote_file, local_path, is_debug=True): - adb_device.sync.pull(remote_file, local_path) - if is_debug: - info(f"pull {remote_file} to {local_path} successful") - -def push_file_to_remote(local_path, remote_path, is_debug=True): - # info(f"push {local_path} to {remote_path}") - from adbutils.errors import AdbError + except ValueError: + pass try: - # 先尝试标准推送 - adb_device.sync.push(local_path, remote_path) - except AdbError as e: - #info("检测到API兼容性问题,降级到更基本的adb push命令") - # 降级到更基本的adb push命令 - subprocess.run( - ["adb", "push", local_path, remote_path], - check=True - ) - if is_debug: - info(f"push {local_path} to {remote_path} successful") - -def is_root(): - output = run_su_command("ls /data/") - return "cache" in output and "user" in output - -def ensure_root(): - if is_root(): + import unicodedata + unicodedata.numeric(s) return True - else: - try: - adb_device.root() # adbutils 内置封装 - if is_root(): - info("Switched to root successfully ✅") - return True - else: - info("❌ Device is not rooted") - return False - except Exception as e: - info(f"❌ Failed to switch to root: {e}") - return False - - -# 自动化部署frida-server -if not is_frida_working_via_attach(): - if not ensure_root(): - info("❌ Cannot deploy frida-server automatically. Please start frida-server manually and try again.") - sys.exit(2) - frida_server_file = choose_frida_server() - remote_frida_server_file = f"/data/mobile-deploy/{frida_server_file}" - if not check_remote_dir_exists("/data/mobile-deploy/"): - run_su_command("mkdir /data/mobile-deploy/") - if not check_remote_file_exists(remote_frida_server_file): - push_file_to_remote(f"mobile-deploy/{frida_server_file}", "/sdcard/") - run_su_command(f"mv /sdcard/{frida_server_file} {remote_frida_server_file}") - run_su_command(f"chmod +x {remote_frida_server_file}") - run_su_command("setenforce 0") - run_su_command(f"/data/mobile-deploy/{choose_frida_server()} -D > /sdcard/f_server.log 2>&1", False) - success = False - for index in range(20): - if is_frida_working_via_attach(): - info("frida-server started successfully ✅") - success = True - break - time.sleep(0.5) - if not success: - info("❌ Failed to start frida-server automatically. Please start it manually and try again.") - sys.exit(2) - -# 全局变量 -current_identifier = None -current_identifier_name = None -current_identifier_version = None -current_identifier_pid = None -current_identifier_install_path = None -current_identifier_install_apkfilename = None -current_identifier_uid = None -current_local_apk_path = None -current_identifier_cache_db = None -current_identifier_cache_readonly_db = None -current_identifier_stop_event = None -webserver_url = None - -frida_device = None - -resource_rpc_jscode = read_js_resource("rpc.js") -resource_hook_js_prepare_jscode = read_js_resource("_hook_js_prepare.js") -resource_hook_js_enhance_jscode = read_js_resource("_hook_js_enhance.js") -resource_hook_js_warp_jscode = read_js_resource("_hook_js_warp.js") - -def _init_resource_jscode(): - global resource_rpc_jscode - global resource_hook_js_prepare_jscode - global resource_hook_js_enhance_jscode - resource_rpc_jscode = read_js_resource("rpc.js") - resource_hook_js_prepare_jscode = read_js_resource("_hook_js_prepare.js") - resource_hook_js_enhance_jscode = read_js_resource("_hook_js_enhance.js") - -_init_resource_jscode() - -def convert_jar_to_dex(jarfile: str) -> bool: - """ - 将 JAR 文件转换为 DEX 文件 - - Args: - jarfile: JAR 文件路径 - - Returns: - 转换成功返回 dex 文件路径,失败返回 None - """ - # 获取 dx/d8 文件路径 - d8_file, dx_file = get_oldest_dx_d8_path() - if dx_file is None and d8_file is None: - warn( - "error: Not found ANDROID_HOME. Please install android sdk and define ANDROID_HOME environment variable in your system") - return None - # 检查输入文件 - if not os.path.isfile(f"{current_identifier}/{jarfile}"): - warn(f"error: JAR file not found: {jarfile}") - return None - # 生成输出文件名(将 .jar 替换为 .dex) - if jarfile.endswith('.jar'): - dexfile = jarfile[:-4] + '.dex' - else: - dexfile = jarfile + '.dex' - out_put_dex_file = f'{current_identifier}/{dexfile}' - try: - if dx_file: - # 使用 dx 命令 - cmd = [dx_file, '--dex', f'--output={out_put_dex_file}', f'{current_identifier}/{jarfile}'] - else: - # 使用 d8 命令 - cmd = [d8_file, '--output', current_identifier, f'{current_identifier}/{jarfile}'] - info(f"Converting {jarfile} to {dexfile}...") - # 执行转换 - result = subprocess.run( - cmd, - capture_output=True, - text=True, - check=True - ) - # 检查输出文件 - if os.path.exists(out_put_dex_file): - file_size = os.path.getsize(out_put_dex_file) - info(f"Successfully converted to {dexfile} ({file_size} bytes)") - return dexfile - else: - info(f"error: Conversion failed, output file not created") - if result.stderr: - info(f"Error output: {result.stderr}") - return None - except subprocess.CalledProcessError as e: - warn(f"error: Conversion failed with exit code {e.returncode}") - if e.stderr: - warn(f"Error output: {e.stderr}") - return None - except Exception as e: - warn(f"error: {str(e)}") - return None - -def _init_frida_device(): - global frida_device - def getRemoteDriver(): - text = io.open(".hooker_driver",'r',encoding= 'utf8').read() - if not text: - return None - searchResult = re.search(r'\d+\.\d+\.\d+\.\d+:\d+', text) - if searchResult: - return searchResult.group() - return None - if frida_device: - return - remoteDriver = getRemoteDriver() #ip:port - if remoteDriver: - frida_device = frida.get_device_manager().add_remote_device(remoteDriver) - else: - frida_device = frida.get_usb_device(1000) - -_init_frida_device() - -@dataclass -class AppInfo: - name: str # 应用名(label,拿不到时用包名代替) - identifier: str # 包名 - pid: Optional[int] # 运行中才有 pid,否则 None - -def _list_third_party_packages() -> List[str]: - """ - 第三方包(-3)最稳定。 - 输出行形如:package:com.xxx.yyy - """ - out = run_su_command("pm list packages -3") - pkgs = [] - for line in out.splitlines(): - line = line.strip() - if line.startswith("package:"): - pkgs.append(line.split("package:", 1)[1]) - return pkgs - - -def _get_pid_map() -> Dict[str, int]: - """ - 获取运行中进程的 pid 映射:package -> pid - 优先 pidof(快),不行再回退 ps。 - """ - pid_map: Dict[str, int] = {} - # 方案 A:pidof(Android 8+ 大多支持) - # pidof 可能返回多个 pid(多进程),这里取第一个 - # 但我们需要逐包调用 pidof,太慢,所以改用 ps 做一次性扫描更划算。 - # 因此优先方案 B:ps -A 解析。 - ps_out = run_su_command("ps -A -o PID,NAME") - # 输出示例: - # PID NAME - # 123 com.xxx.yyy - for line in ps_out.splitlines()[1:]: - line = line.strip() - if not line: - continue - parts = re.split(r"\s+", line, maxsplit=1) - if len(parts) != 2: + except (TypeError, ValueError): + pass + return False + +def getPidMap(): + pidMap = {} + lines = os.popen("frida-ps -U").readlines(); + for line in lines: + result = re.search("(\d+)\s+([a-z\d\.]+)($|:)", line.strip()) + if not result: continue - pid_s, name = parts - if name.startswith("com."): # 简单过滤:只收集 app 进程 - try: - pid_map[name] = int(pid_s) - except ValueError: - pass - return pid_map - - -def _get_app_label_fast(pkg: str) -> Optional[str]: - """ - 尝试快速拿 label(不保证所有 ROM 都能拿到)。 - 方法:cmd package dump + grep - 失败就返回 None(用包名代替)。 - """ - # 有些系统支持:cmd package resolve-activity / dumpsys package - # 但 label 不一定直接给;dumpsys package 里通常能找到 "application-label:" - out = run_su_command(f"dumpsys package {pkg} | grep -m 1 -E 'application-label:'") - m = re.search(r"application-label:'(.*)'", out) - if m: - return m.group(1).strip() - return pkg - - -def enumerate_applications_adbutils(third_party_only: bool = True, include_label: bool = False) -> List[AppInfo]: - """ - 用 adbutils 实现一个“类 enumerate_applications”的方法: - - identifier: 包名 - - pid: 运行中的进程 pid(取主进程名 == 包名的情况) - - name: 可选,从 dumpsys 拿 label;默认用包名代替(快很多) - """ - apps: List[AppInfo] = [] - # pkgs = _list_third_party_packages() - # info(f"pkgs: {pkgs}") - # pid_map = _get_pid_map() - # info(f"is_magisk_root3:{is_magisk_root}") - # for pkg in pkgs: - # pid = pid_map.get(pkg, 0) # 只有“进程名==包名”的主进程才会命中 - # if include_label: - # info(f"_get_app_label_fast:{pkg}") - # label = _get_app_label_fast(pkg) or pkg - # else: - # label = pkg - # apps.append(AppInfo(name=label, identifier=pkg, pid=pid)) - # info(f"enumerate_applications_adbutils: {apps}") - return frida_device.enumerate_applications() - -def start_app(package_name): - global current_identifier_pid - shell_result = adb_device.shell(f"dumpsys package {package_name} | grep -A 1 MAIN | grep {package_name}").strip() - m = re.search(r"\s+([^\s]+)\s+filter", shell_result) - if m: - main_activity = m.group(1) - adb_device.shell(f"am start -n {main_activity}") - else: - adb_device.shell(f"monkey -p {package_name} -c android.intent.category.LAUNCHER 1") - for j in range(20): - time.sleep(0.5) - out = adb_device.shell(f"pidof {package_name}").strip() - if out and out.isdigit(): - current_identifier_pid = int(out) - return current_identifier_pid, current_identifier_name - return None, None - -def restart_app(package_name): - global current_identifier_pid - info(f"Restarting {current_identifier_name} Please wait for a few seconds") - adb_device.app_stop(package_name) - time.sleep(1) - app_pid, app_name = start_app(package_name) - current_identifier_pid = app_pid - -def ensure_app_in_foreground(package_name): - def get_main_pid(pkg): - out = adb_device.shell(f"pidof {pkg}").strip() - if out: - return int(out.split()[0]) - return None - uid = None - shell_result = adb_device.shell(f"dumpsys package {package_name}").strip() - matchx = re.search(r"(userId|uid|appId)=(\d+)", shell_result) - if matchx: - uid = int(matchx.group(2)) - else: - warn("UID not found.") - apk_path = adb_device.shell(f"pm path {package_name}").strip().replace("package:", "") - if "priv-app" in apk_path: - apk_path = apk_path[:apk_path.index(".apk")+8] - else: - apk_path = apk_path[:apk_path.index("base.apk")+8] - #print(f"apk_path:{apk_path}") - appinstall_path = apk_path.rsplit("/", 1)[0] - appinstall_path_apkfilename = apk_path.rsplit("/", 1)[1] - appinfo = None - version_name = None - if 'app_info' in dir(adb_device): - appinfo = adb_device.app_info(package_name) - version_name = appinfo.version_name - else: - appinfo = adb_device.package_info(package_name) - version_name = appinfo["version_name"] - # 获取当前正在运行的所有进程 - proc_map = {} - apps = enumerate_applications_adbutils(third_party_only=True, include_label=True) - for app in sorted(apps, key=lambda x: x.pid or 0): - if app.pid != 0: # 只列出运行中的 - proc_map[app.identifier] = (app.pid, app.name) - is_running = package_name in proc_map - # 获取当前前台 activity - foreground_output = adb_device.shell("dumpsys activity activities | grep mResumedActivity") - is_foreground = package_name in foreground_output - if is_running: - if is_foreground: - info(f"✅ App {package_name} is already in the foreground") - else: - info(f"📲 App {package_name} is running in the background, bringing it to the foreground...") - # 通过 am 启动主 Activity,会自动 bring 到前台 - adb_device.shell(f"monkey -p {package_name} -c android.intent.category.LAUNCHER 1") - #print("proc_map[package_name][1]", proc_map[package_name][1]) - main_pid = get_main_pid(package_name) - app_pid = main_pid if main_pid is not None else proc_map[package_name][0] - return app_pid, proc_map[package_name][1], version_name, appinstall_path, appinstall_path_apkfilename, uid - else: - info(f"🚀 App {package_name} is not running, starting it now...") - #adb_device.shell(f"monkey -p {package_name} -c android.intent.category.LAUNCHER 1") - app_pid, app_name = start_app(package_name) - main_pid = get_main_pid(package_name) - app_pid = main_pid if main_pid is not None else app_pid - return app_pid, app_name, version_name, appinstall_path, appinstall_path_apkfilename, uid - -def get_remote_file_md5(file_path): - # 检查文件是否存在并获取长度 - check_cmd = f"md5sum {file_path}" - result = run_su_command(check_cmd).strip() - if "No such file" in result or "Permission denied" in result or not result: - #warn("No such file") - return "" + pidMap[result.group(1)] = result.group(2) + return pidMap + +#target可以是pid或者packageName +def attach(target): + packageName = target + if is_number(target):#pid + packageName = getPidMap()[target] + online_session = None + online_script = None + rdev = None + remoteDriver = run_env.getRemoteDriver() #ip:port try: - # 56cf2745f4884b4dfcc1e193d0118c05 radar.dex - m = re.search(r"[\w]{32}", result) - if m: - return m.group() + if remoteDriver: + rdev = frida.get_device_manager().add_remote_device(remoteDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + #print(f"attach {target}") + if is_number(target): + pid = int(target) + online_session = frida.core.Session(rdev._impl.attach(pid)) else: - return "" + online_session = rdev.attach(target) + if online_session == None: + warn("attaching fail to " + target) + online_script = online_session.create_script(run_env.rpc_jscode) + online_script.on('message', on_message) + online_script.load() + checkRadarDex(packageName, online_script) + createHookingEnverment(packageName, online_script.exports.mainactivity()) except Exception: - return "" + warn(traceback.format_exc()) + return online_session,online_script,packageName -def get_local_file_md5(filepath, chunk_size=8192): - md5 = hashlib.md5() - try: - with open(filepath, 'rb') as f: - while True: - chunk = f.read(chunk_size) - if not chunk: - break - md5.update(chunk) - return md5.hexdigest() - except FileNotFoundError: - warn(f"File Not Found: {filepath}") - return None - -def read_local_file(filename): - return io.open(filename,'r',encoding= 'utf8').read() - -def check_dependency_files(): - def process_dex_dependency_files(): - compara_and_update_file("mobile-deploy/radar.dex", "/data/local/tmp/radar.dex") - t = threading.Thread(target=process_dex_dependency_files) - t.daemon = True - t.start() - -def compara_and_update_file(local_file, remote_file): - local_md5 = get_local_file_md5(local_file) - local_filename = local_file.split("/")[-1] - sdcard_remote_md5 = get_remote_file_md5(f"/sdcard/{local_filename}") - #先把radar.dex拷贝到sdcard,后期更新radar.dex直接从sdcard拷过去 - if local_md5 != sdcard_remote_md5: - push_file_to_remote(local_file, "/sdcard/", False) - remote_md5 = get_remote_file_md5(remote_file) - if local_md5 != remote_md5: - run_su_command(f"cp '/sdcard/{local_filename}' '{remote_file}'") - run_su_command(f"chmod 555 '{remote_file}'") - remote_md5 = get_remote_file_md5(remote_file) - if local_md5 != remote_md5: - warn(f"push file failed: {remote_file}") - return False - return True - -def on_message(message, data): - if message['type'] == 'send': - print("[*] {0}".format(message['payload'])) - elif message['type'] == 'error': - warn("[!] {0}".format(message['stack'])) - else: - print(message) - -def attach_rpc(use_v8=False): - global frida_device +def detach(online_session): + if online_session != None: + online_session.detach() + +def existsClass(target,className): online_session = None online_script = None - online_session = frida_device.attach(current_identifier_pid) - if online_session == None: - warn("attaching fail to device") - return None, None - if use_v8: - online_script = online_session.create_script(resource_rpc_jscode, runtime="v8") - else: - online_script = online_session.create_script(resource_rpc_jscode) - online_script.on('message', on_message) - online_script.load() - # online_script.exports_sync.loadradardex() - return online_session, online_script + try: + online_session,online_script,_ = attach(target); + info(online_script.exports.containsclass(className)) + except Exception: + warn(traceback.format_exc()) + finally: + detach(online_session) -def attach(script_file, use_v8=False): - if not os.path.isfile(script_file): - warn(f"attach {script_file} File Not found") - return None, None - script_jscode = read_local_file(script_file) - script_jscode = script_jscode + "\n\n\n" + resource_hook_js_warp_jscode - global frida_device +def findclasses(target, classRegex): online_session = None online_script = None - online_session = frida_device.attach(current_identifier_pid) - if online_session == None: - warn("attaching fail to device") - return None, None - if use_v8: - #info("use v8 js engine") - online_script = online_session.create_script(script_jscode, runtime="v8") - else: - online_script = online_session.create_script(script_jscode) - online_script.on('message', on_message) - online_script.load() - #sys.stdin.read() - return online_session, online_script - -def spawn(script_file, use_v8=False): - if not os.path.isfile(script_file): - warn(f"{script_file} File Not found") - return None, None - script_jscode = read_local_file(script_file) - script_jscode = script_jscode + "\n\n\n" + resource_hook_js_warp_jscode - global frida_device - current_identifier_pid = frida_device.spawn([current_identifier]) - online_script = None - online_session = frida_device.attach(current_identifier_pid) - if online_session == None: - warn("attaching fail to device") - return None, None - if use_v8: - online_script = online_session.create_script(script_jscode, runtime="v8") - else: - online_script = online_session.create_script(script_jscode) - online_script.on('message', on_message) - online_script.load() - release_version = int(adb_device.prop.get("ro.build.version.release").split('.', 1)[0]) - if release_version >= 12: - frida_device.resume(current_identifier_pid) - else: - frida_device.resume(current_identifier) - #sys.stdin.read() - return online_session, online_script - + try: + online_session,online_script,_ = attach(target); + info(online_script.exports.findclasses(classRegex)); + except Exception: + warn(traceback.format_exc()) + finally: + detach(online_session) -def detach(online_session, online_script): - if online_script != None: - try: - online_script.exports_sync.cleanup() - online_script.unload() - except Exception as e: - info(f"RPC cleanup failed: {e}") - if online_session != None: - try: - online_session.detach() - except Exception as e: - info(f"detach failed: {e}") - - -def exists_class(target, className): +def findclasses2(target, className): online_session = None online_script = None try: - online_session, online_script,_ = attach_rpc(target); - info(online_script.exports_sync.containsclass(className)) + online_session,online_script,_ = attach(target); + info(online_script.exports.findclasses2(className)); except Exception: warn(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) -def create_workingdir_file(filename, text): +def createFile(filename, text): file = None try: file = open(filename, mode='w+') @@ -879,1461 +151,283 @@ def create_workingdir_file(filename, text): if file != None: file.close() -def create_working_dir_enverment(): - global current_identifier - global frida_device - global current_identifier_name - global current_identifier_version - packageName = current_identifier +def onlyCheckHookingEnverment(target): + online_session = None + try: + online_session,_,_ = attach(target); + except Exception: + print(traceback.format_exc()) + finally: + detach(online_session) + +def createHookingEnverment(packageName, mainActivity): if not os.path.exists(packageName): os.makedirs(packageName) - info(f"Creating working directory: {packageName}") - info(f"Generating frida shortcut command...") os.makedirs(packageName+"/xinit") shellPrefix = "#!/bin/bash\nHOOKER_DRIVER=$(cat ../.hooker_driver)\n" - logHooking = shellPrefix + "echo \"hooking $1\" > log\ndate | tee -ai log\n" + "frida $HOOKER_DRIVER -l $1 -N " + packageName + " | tee -ai log" - attach_shell = shellPrefix + "frida $HOOKER_DRIVER -l $1 -N " + packageName - spawn_shell = f"{shellPrefix}\nfrida $HOOKER_DRIVER --runtime=v8 -f {packageName} -l $1" - create_workingdir_file(packageName+"/hooking", logHooking) - create_workingdir_file(packageName+"/attach", attach_shell) - create_workingdir_file(packageName+"/spawn", spawn_shell) - create_workingdir_file(packageName + "/kill", shellPrefix + "frida-kill $HOOKER_DRIVER "+packageName) - create_workingdir_file(packageName+"/objection", shellPrefix + "objection -d -g "+packageName+" explore") + logHooking = shellPrefix + "echo \"hooking $1\" > log\ndate | tee -ai log\n" + "frida $HOOKER_DRIVER -l $1 " + packageName + " | tee -ai log" + attach_shell = shellPrefix + "frida $HOOKER_DRIVER -l $1 " + packageName + spawn_shell = f"{shellPrefix}\nfrida $HOOKER_DRIVER --no-pause -f {packageName} -l $1" + xinitPyScript = run_env.xinitPyScript + "xinitDeploy('"+packageName+"')" + spiderPyScript = run_env.spiderPyScript.replace("{appPackageName}", packageName).replace("{mainActivity}", mainActivity) + disableSslPinningPyScript = run_env.disableSslPinningPyScript.replace("{appPackageName}", packageName) + createFile(packageName+"/hooking", logHooking) + createFile(packageName+"/attach", attach_shell) + createFile(packageName+"/spawn", spawn_shell) + createFile(packageName+"/xinitdeploy", xinitPyScript) + createFile(packageName+"/disable_sslpinning", disableSslPinningPyScript) + createFile(packageName+"/spider.py", spiderPyScript) + createFile(packageName + "/kill", shellPrefix + "frida-kill $HOOKER_DRIVER "+packageName) + createFile(packageName+"/objection", shellPrefix + "objection -d -g "+packageName+" explore") os.popen('chmod 777 ' + packageName +'/hooking').readlines() os.popen('chmod 777 ' + packageName +'/attach').readlines() + os.popen('chmod 777 ' + packageName +'/xinitdeploy').readlines() + os.popen('chmod 777 ' + packageName +'/disable_sslpinning').readlines() + os.popen('chmod 777 ' + packageName +'/kill').readlines() os.popen('chmod 777 ' + packageName +'/objection').readlines() os.popen('chmod 777 ' + packageName +'/spawn').readlines() - info(f"Generating built-in frida script...") - init_js_files = [ - "url.js", - "android_ui.js", - "edit_text.js", - "text_view.js", - "click.js", - "activity_events.js", - "object_store.js", - "keystore_dump.js", - "ssl_log.js", - "just_trust_me.js", - "just_trust_me_for_ios.js", - "hook_register_natives.js", - "dump_dex.js", - "trace_init_proc.js", - "hook_artmethod_register.js", - "find_anit_frida_so.js", - "jni_method_trace.js", - "replace_dlsym_get_pthread_create.js", - "find_boringssl_custom_verify_func.js", - "get_device_info.js", - "apk_shell_scanner.js", - "bypass_frida_svc_detect.js", - "bypass_root_detect.js", - "bypass_vpn_detect.js", - "hook_encryption_algo.js", - "hook_encryption_algo2.js", - "webview_enable_debug.js", - "hook_proxy_check.js" - ] - for js_file in init_js_files: - if not os.path.exists(f"js/{js_file}"): - info(f"File not Found: js/{js_file}") - continue - jscode = read_js_resource(js_file) - create_workingdir_file(f"{packageName}/{js_file}", jscode.replace("com.smile.gifmaker", packageName)) - info(f"Copying APK {current_identifier_install_path}/{current_identifier_install_apkfilename} to working directory please waiting for a few seconds") - global current_local_apk_path - current_local_apk_path = f"{packageName}/{current_identifier_name.replace(' ', '')}_{current_identifier_version}.apk" - pull_file_to_local(f"{current_identifier_install_path}/{current_identifier_install_apkfilename}", current_local_apk_path) - info(f"Working directory create successful") - -def init_working_dir_enverment(): - global current_local_apk_path - current_local_apk_path = f"{current_identifier}/{current_identifier_name.replace(' ', '')}_{current_identifier_version}.apk" - if os.path.isfile(current_local_apk_path): - return - if os.path.isdir(current_local_apk_path): - os.popen(f'rm -rf {current_local_apk_path}').readlines() - #print(f"current_identifier_install_path:{current_identifier_install_path}") - pull_file_to_local(f"{current_identifier_install_path}/{current_identifier_install_apkfilename}", current_local_apk_path) - info(f"Working directory init successful") - -def hook_js(hookCmdArg, savePath = None): + createFile(packageName + "/ssl_log.js", run_env.ssl_log_jscode) + createFile(packageName + "/url.js", run_env.url_jscode) + createFile(packageName + "/edit_text.js", run_env.edit_text_jscode) + createFile(packageName + "/text_view.js", run_env.text_view_jscode) + createFile(packageName + "/click.js", run_env.click_jscode) + createFile(packageName + "/hook_RN.js", run_env.hook_RN_jscode) + createFile(packageName + "/keystore_dump.js", run_env.keystore_dump_jscode) + createFile(packageName + "/dump_dex.js", run_env.dump_dex_jscode) + createFile(packageName + "/android_ui.js", run_env.android_ui_jscode.replace("com.smile.gifmaker", packageName)) + createFile(packageName + "/activity_events.js", run_env.activity_events_jscode.replace("com.smile.gifmaker", packageName)) + createFile(packageName + "/object_store.js", run_env.object_store_jscode.replace("com.smile.gifmaker", packageName)) + createFile(packageName + "/just_trust_me.js", run_env.just_trust_me_jscode.replace("com.smile.gifmaker", packageName)) + createFile(packageName + "/just_trust_me_okhttp_hook_finder.js", run_env.just_trust_me_okhttp_hook_finder_jscode.replace("com.smile.gifmaker", packageName)) + +def hookJs(target, hookCmdArg, savePath = None): online_session = None online_script = None - packageName = current_identifier + packageName = None try: ganaretoionJscode = "" - online_session, online_script = attach_rpc(use_v8=False); - appversion = current_identifier_version - spaceSpatrater = hookCmdArg.find(":") - className = hookCmdArg - toSpace = "*" - file_method_name = "allfunc" - if spaceSpatrater > 0: - className = hookCmdArg[:spaceSpatrater] - toSpace = hookCmdArg[spaceSpatrater+1:] - if "" in toSpace: - toSpace = "_" - file_method_name = "_init" - else: - toSpace = toSpace.split("(")[0] - file_method_name = toSpace - if not online_script.exports_sync.containsclass(className): - warn(f"Class Not Found {className}") - return - jscode = online_script.exports_sync.hookjs(className, toSpace); - ganaretoionJscode += ("\n//"+hookCmdArg+"\n") - ganaretoionJscode += jscode + online_session,online_script,packageName = attach(target); + appversion = online_script.exports.appversion(); + classes = hookCmdArg.split(",") + for classN in classes: + spaceSpatrater = classN.find(":") + className = classN + toSpace = "*" + if spaceSpatrater > 0: + className = classN[:spaceSpatrater] + toSpace = classN[spaceSpatrater+1:] + jscode = online_script.exports.hookjs(className, toSpace); + ganaretoionJscode += ("\n//"+classN+"\n") + ganaretoionJscode += jscode + if savePath == None: - defaultFilename = className.replace(":", ".").replace("$", ".").replace("__", ".")+ "." + file_method_name + ".js" + defaultFilename = hookCmdArg.replace(".", "_").replace(":", "_").replace("$", "_").replace("__", "_") + ".js" savePath = packageName+"/"+defaultFilename; else: - defaultFilename = savePath savePath = packageName+"/"+savePath; if len(ganaretoionJscode): - ganaretoionJscode = resource_hook_js_prepare_jscode + "\n" + ganaretoionJscode + "\n\n\n\n\n//---------------------may be you need--------------------\n\n" + resource_hook_js_enhance_jscode - warpExtraInfo = f"//cracked by {current_identifier_name} {appversion}\n" - warpExtraInfo += "//"+hookCmdArg + "\n\n" + ganaretoionJscode = run_env.loadxinit_dexfile_template_jscode.replace("{PACKAGENAME}", packageName) + "\n" + ganaretoionJscode + warpExtraInfo = "//crack by " + packageName + " " + appversion + "\n" + warpExtraInfo += "//"+hookCmdArg + "\n" + warpExtraInfo += run_env.base_jscode warpExtraInfo += ganaretoionJscode - create_workingdir_file(savePath, jsbeautifier.beautify(warpExtraInfo)) - info("frida hook script: " + defaultFilename) + createFile(savePath, warpExtraInfo) + info("Hooking js code have generated. Path is " + savePath+".") else: warn("Not found any classes by pattern "+hookCmdArg+".") except Exception: warn(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) -def print_activitys(): +def hookStr(target, keyword): online_session = None - online_script = None + packageName = None try: - online_session,online_script = attach_rpc(); - info(online_script.exports_sync.activitys()) + online_session,_,packageName = attach(target); + jscode = io.open('./js/string_hooker.js','r',encoding= 'utf8').read() + jscode = jscode.replace("惊雷", keyword) + savePath = packageName+"/str_"+keyword+".js"; + createFile(savePath, jscode) + info("Hooking js code have generated. Path is " + savePath+".") except Exception: print(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) -def print_services(): + +def hookParma(target, keyword): online_session = None - online_script = None + packageName = None try: - online_session, online_script = attach_rpc(); - info(online_script.exports_sync.services()) + online_session,_,packageName = attach(target); + jscode = io.open('./js/param_hook.js','r',encoding= 'utf8').read() + jscode = jscode.replace("NStokensig", keyword) + savePath = packageName+"/param_"+keyword+".js"; + createFile(savePath, jscode) + info("Hooking js code have generated. Path is " + savePath+".") except Exception: print(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) + -def print_object(objectId): +def printActivitys(target): online_session = None online_script = None try: - online_session, online_script = attach_rpc(); - info(online_script.exports_sync.objectinfo(objectId)) + online_session,online_script,_ = attach(target); + info(online_script.exports.activitys()) except Exception: print(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) -def object_to_explain(objectId): +def printServices(target): online_session = None online_script = None try: - online_session, online_script = attach_rpc(); - info(online_script.exports_sync.objecttoexplain(objectId)) + online_session,online_script,_ = attach(target); + info(online_script.exports.services()) except Exception: print(traceback.format_exc()) finally: - detach(online_session, online_script) + detach(online_session) -def print_view(viewId): +def printObject(target, objectId): online_session = None online_script = None try: - online_session, online_script = attach_rpc(); - report = online_script.exports_sync.viewinfo(viewId) - info(report); + online_session,online_script,_ = attach(target); + info(online_script.exports.objectinfo(objectId)) except Exception: print(traceback.format_exc()) finally: - detach(online_session, online_script) - -def rpc_start_web_server(dex_file, all_class): - global webserver_url + detach(online_session) + +def object2Explain(target, objectId): online_session = None online_script = None try: - online_session, online_script = attach_rpc(); - text = online_script.exports_sync.starthttpserver(dex_file, ",".join(all_class)) - info(text) - m = re.search("http:[^s]+:[\d]+", text) - if m: - webserver_url = m.group(0) - else: - info("找不到webserver" + text) + online_session,online_script,_ = attach(target); + info(online_script.exports.objecttoexplain(objectId)) except Exception: - print(traceback.format_exc()) + print(traceback.format_exc()) finally: - detach(online_session, online_script) - + detach(online_session) -def list_working_dir(): - js_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".js") - } - print_js_file(list(js_files.keys())) - - -def execute_script(script_file, is_spawn=False): - if not os.path.isfile(f"{current_identifier}/{script_file}"): - warn(f"{current_identifier}/{script_file} File Not found") - return +def printView(target, viewId): online_session = None online_script = None - log_fh = None - saved_fd = None - pipe_r = None - pipe_w = None - tee_thread = None - use_v8 = "just_trust_me.js" in script_file try: - log_filename = script_file.rsplit('.', 1)[0] + '.log' - log_filepath = f"{current_identifier}/{log_filename}" - log_fh = open(log_filepath, 'w', encoding='utf-8') - # fd 级别重定向,捕获所有 C 层写 stdout 的输出 - pipe_r, pipe_w = os.pipe() - saved_fd = os.dup(1) - os.dup2(pipe_w, 1) - os.close(pipe_w) - pipe_w = None - stop_event = threading.Event() - def tee_reader(): - while not stop_event.is_set(): - try: - data = os.read(pipe_r, 8192) - if not data: - break - os.write(saved_fd, data) - log_fh.write(data.decode('utf-8', errors='replace')) - log_fh.flush() - except Exception: - break - tee_thread = threading.Thread(target=tee_reader, daemon=True) - tee_thread.start() - if is_spawn: - online_session, online_script = spawn(f"{current_identifier}/{script_file}", use_v8) - else: - online_session, online_script = attach(f"{current_identifier}/{script_file}", use_v8) - info(f"Frida output logging -> {log_filepath}") - while online_session != None: - try: - with patch_stdout(): - text = cmd_session.prompt("CTRL + C to stop > ", handle_sigint=True) - except KeyboardInterrupt: - info(f"Interrupting {script_file}") - break - except EOFError: - warn("Exiting...") - break + online_session,online_script,_ = attach(target); + report = online_script.exports.viewinfo(viewId) + info(report); except Exception: - print(traceback.format_exc()) + print(traceback.format_exc()) finally: - detach(online_session, online_script) - # 恢复 fd 1 - if saved_fd is not None: - os.dup2(saved_fd, 1) - os.close(saved_fd) - if pipe_r is not None: - os.close(pipe_r) - if tee_thread is not None: - stop_event.set() - if log_fh is not None: - log_fh.close() - info(f"{script_file} detach successful") - if is_spawn: - restart_app(current_identifier) - -def just_trust_me(): - execute_script("just_trust_me.js", True) - - -def r0capture(): - PY3K = sys.version_info >= (3, 0) - # --- workaround against Python consistency issues - def normalize_py(): - if sys.platform == "win32": - # set sys.stdout to binary mode on Windows - import os, msvcrt - msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) - - # --- - chunking helpers - def chunks(seq, size): - d, m = divmod(len(seq), size) - for i in range(d): - yield seq[i * size:(i + 1) * size] - if m: - yield seq[d * size:] - - - def chunkread(f, size): - c = f.read(size) - while len(c): - yield c - c = f.read(size) - - - def genchunks(mixed, size): - if hasattr(mixed, 'read'): - return chunkread(mixed, size) - else: - return chunks(mixed, size) - - - # --- - /chunking helpers - def dehex(hextext): - if PY3K: - return bytes.fromhex(hextext) - else: - hextext = "".join(hextext.split()) - return hextext.decode('hex') + detach(online_session) - def dump(binary, size=2, sep=' '): - hexstr = binascii.hexlify(binary) - if PY3K: - hexstr = hexstr.decode('ascii') - return sep.join(chunks(hexstr.upper(), size)) - - - def dumpgen(data, only_str): - generator = genchunks(data, 16) - for addr, d in enumerate(generator): - line = "" - if not only_str: - # 00000000: - line = '%08X: ' % (addr * 16) - # 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - dumpstr = dump(d) - line += dumpstr[:8 * 3] - if len(d) > 8: # insert separator if needed - line += ' ' + dumpstr[8 * 3:] - # ................ - # calculate indentation, which may be different for the last line - pad = 2 - if len(d) < 16: - pad += 3 * (16 - len(d)) - if len(d) <= 8: - pad += 1 - line += ' ' * pad - - for byte in d: - # printable ASCII range 0x20 to 0x7E - if not PY3K: - byte = ord(byte) - if 0x20 <= byte <= 0x7E: - line += chr(byte) - else: - line += '.' - yield line - - - def hexdump(data, result='print', only_str=False): - if PY3K and type(data) == str: - raise TypeError('Abstract unicode data (expected bytes sequence)') - gen = dumpgen(data, only_str=only_str) - if result == 'generator': - return gen - elif result == 'return': - return '\n'.join(gen) - elif result == 'print': - for line in gen: - print(line) - else: - raise ValueError('Unknown value of `result` argument') - banner = ( - "--------------------------------------------------------------------------------------------\n" - " .oooo. . \n" - " d8P'`Y8b .o8 \n" - "oooo d8b 888 888 .ooooo. .oooo. oo.ooooo. .o888oo oooo oooo oooo d8b .ooooo. \n" - "`888\"\"8P 888 888 d88' `\"Y8 `P )88b 888' `88b 888 `888 `888 `888\"\"8P d88' `88b \n" - " 888 888 888 888 .oP\"888 888 888 888 888 888 888 888ooo888 \n" - " 888 `88b d88' 888 .o8 d8( 888 888 888 888 . 888 888 888 888 .o \n" - "d888b `Y8bd8P' `Y8bod8P' `Y888\"\"8o 888bod8P' \"888\" `V88V\"V8P' d888b `Y8bod8P' \n" - " 888 \n" - " o888o \n" - " https://github.com/r0ysue/r0capture\n" - "--------------------------------------------------------------------------------------------\n" - ) - print(banner) - ssl_sessions = {} - def ssl_log(pcap=None, verbose=False, ssllib="", wait=0): - # if platform.system() not in ("Darwin", "Linux"): - # raise NotImplementedError("This function is only implemented for Linux and " - # "macOS systems.") - - def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port, - dst_addr, dst_port, data): - t = time.time() - if ssl_session_id not in ssl_sessions: - ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF), - random.randint(0, 0xFFFFFFFF)) - client_sent, server_sent = ssl_sessions[ssl_session_id] - if function == "SSL_read": - seq, ack = (server_sent, client_sent) - else: - seq, ack = (client_sent, server_sent) - for writes in ( - # PCAP record (packet) header - ("=I", int(t)), # Timestamp seconds - ("=I", int((t * 1000000) % 1000000)), # Timestamp microseconds - ("=I", 40 + len(data)), # Number of octets saved - ("=i", 40 + len(data)), # Actual length of packet - # IPv4 header - (">B", 0x45), # Version and Header Length - (">B", 0), # Type of Service - (">H", 40 + len(data)), # Total Length - (">H", 0), # Identification - (">H", 0x4000), # Flags and Fragment Offset - (">B", 0xFF), # Time to Live - (">B", 6), # Protocol - (">H", 0), # Header Checksum - (">I", src_addr), # Source Address - (">I", dst_addr), # Destination Address - # TCP header - (">H", src_port), # Source Port - (">H", dst_port), # Destination Port - (">I", seq), # Sequence Number - (">I", ack), # Acknowledgment Number - (">H", 0x5018), # Header Length and Flags - (">H", 0xFFFF), # Window Size - (">H", 0), # Checksum - (">H", 0)): # Urgent Pointer - pcap_file.write(struct.pack(writes[0], writes[1])) - pcap_file.write(data) - if function == "SSL_read": - server_sent += len(data) - else: - client_sent += len(data) - ssl_sessions[ssl_session_id] = (client_sent, server_sent) - - def r0capture_on_message(message, data): - if message["type"] == "error": - logger.info(f"{message}") - os.kill(os.getpid(), signal.SIGTERM) - return - if len(data) == 1: - logger.info(f'{message["payload"]["function"]}') - logger.info(f'{message["payload"]["stack"]}') - return - p = message["payload"] - if verbose: - src_addr = socket.inet_ntop(socket.AF_INET, - struct.pack(">I", p["src_addr"])) - dst_addr = socket.inet_ntop(socket.AF_INET, - struct.pack(">I", p["dst_addr"])) - session_id = p['ssl_session_id'] - logger.info(f"SSL Session: {session_id}") - logger.info("[%s] %s:%d --> %s:%d" % ( - p["function"], - src_addr, - p["src_port"], - dst_addr, - p["dst_port"])) - gen = hexdump(data, result="generator",only_str=True) - str_gen = ''.join(gen) - logger.info(f"{str_gen}") - logger.info(f"{p['stack']}") - if pcap: - log_pcap(pcap_file, p["ssl_session_id"], p["function"], p["src_addr"], - p["src_port"], p["dst_addr"], p["dst_port"], data) - current_identifier_pid = frida_device.spawn([current_identifier]) - online_session = frida_device.attach(current_identifier_pid) - time.sleep(1) - if wait > 0: - print(f"wait for {wait} seconds") - time.sleep(wait) - - if pcap: - pcap_file = open(pcap, "wb", 0) - for writes in ( - ("=I", 0xa1b2c3d4), # Magic number - ("=H", 2), # Major version number - ("=H", 4), # Minor version number - ("=i", time.timezone), # GMT to local correction - ("=I", 0), # Accuracy of timestamps - ("=I", 65535), # Max length of captured packets - ("=I", 228)): # Data link type (LINKTYPE_IPV4) - pcap_file.write(struct.pack(writes[0], writes[1])) - r0capture_script = read_js_resource("r0capture.js") - online_script = online_session.create_script(r0capture_script, runtime="v8") - online_script.on("message", r0capture_on_message) - online_script.load() - release_version = int(adb_device.prop.get("ro.build.version.release").split('.', 1)[0]) - if release_version >= 12: - frida_device.resume(current_identifier_pid) - else: - frida_device.resume(current_identifier) - if ssllib != "": - online_script.exports.setssllib(ssllib) - try: - while online_session != None: - try: - with patch_stdout(): - text = cmd_session.prompt("CTRL + C to stop > ", handle_sigint=True) - except KeyboardInterrupt: - info(f"Interrupting") - break - except EOFError: - warn("Exiting...") - break - except Exception: - print(traceback.format_exc()) - finally: - detach(online_session, online_script) - if pcap: - pcap_file.flush() - pcap_file.close() - info(f"flushing {current_identifier}/r0capture_ssl.pcap successful") - info("r0capture.js detach successful") - restart_app(current_identifier) - ssl_log(f"{current_identifier}/r0capture_ssl.pcap", True) - -def un_proxy(): - run_su_command(r"for i in $(iptables -t nat -L OUTPUT --line-numbers | grep REDIRECT |grep 12345 | awk \"{print \$1}\" | sort -rn); do iptables -t nat -D OUTPUT $i; done") - run_su_command("iptables -t nat -F REDSOCKS") - run_su_command("iptables -t nat -D OUTPUT -p tcp -j REDSOCKS") - run_su_command("iptables -t nat -X REDSOCKS") - run_su_command("killall redsocks") - run_su_command("pid=$(ps -ef | grep '[r]edsocks' | awk '{print $2}'); [ -n \"$pid\" ] && kill -9 $pid") - -def set_proxy(proxy): - pattern = r'(http|socks5)://(\d{2,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)$' - m = re.search(pattern, proxy.strip()) - if not m: - warn(f"proxy scheme error: {proxy}") - return - proxy_type = m.group(1) - if proxy_type == "http": - proxy_type = "http-relay" - proxy_ip = m.group(2) - proxy_port = m.group(3) - config = ( - "base {\n" - " log_debug = on;\n" - " log_info = on;\n" - " daemon = on;\n" - " redirector = iptables;\n" - "}\n\n" - ) - if proxy_type == "http-relay": - config += ( - "redsocks {\n" - " local_ip = 127.0.0.1;\n" - " local_port = 12345;\n" - f" ip = {proxy_ip};\n" - f" port = {proxy_port};\n" - f" type = http-relay;\n" - "}" - ) - else: - config += ( - "redsocks {\n" - " local_ip = 127.0.0.1;\n" - " local_port = 12345;\n" - f" ip = {proxy_ip};\n" - f" port = {proxy_port};\n" - f" type = {proxy_type};\n" - "}" - ) - if not check_remote_file_exists("/sdcard/redsocks"): - push_file_to_remote(f"mobile-deploy/redsocks", "/sdcard/redsocks", False) - run_su_command(f"cp /sdcard/redsocks /data/local/tmp/redsocks") - run_su_command(f"chmod 700 /data/local/tmp/redsocks") - if not check_remote_file_exists("/sdcard/libevent-2.1.so"): - push_file_to_remote(f"mobile-deploy/libevent-2.1.so", "/sdcard/libevent-2.1.so", False) - run_su_command(f"cp /sdcard/libevent-2.1.so /data/local/tmp/libevent-2.1.so") - if not check_remote_file_exists("/sdcard/libevent_core-2.1.so"): - push_file_to_remote(f"mobile-deploy/libevent_core-2.1.so", "/sdcard/libevent_core-2.1.so", False) - run_su_command(f"cp /sdcard/libevent_core-2.1.so /data/local/tmp/libevent_core-2.1.so") - if not check_remote_file_exists("/data/local/tmp/redsocks"): - run_su_command(f"cp /sdcard/redsocks /data/local/tmp/redsocks") - run_su_command(f"cp /sdcard/libevent-2.1.so /data/local/tmp/libevent-2.1.so") - run_su_command(f"cp /sdcard/libevent_core-2.1.so /data/local/tmp/libevent_core-2.1.so") - run_su_command(f"chmod 700 /data/local/tmp/redsocks") - un_proxy() - adb_device.shell(f"rm -f /data/local/tmp/redsocks.conf") - adb_device.shell(f"echo '{config}' > /data/local/tmp/redsocks.conf") - time.sleep(1) - run_su_command(f"LD_LIBRARY_PATH=/data/local/tmp/ /data/local/tmp/redsocks -c /data/local/tmp/redsocks.conf") - if proxy_type == "http-relay": - run_su_command(f"iptables -t nat -N REDSOCKS") - run_su_command(f"iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN") - run_su_command(f"iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN") - run_su_command(f"iptables -t nat -A REDSOCKS -d 192.168.1.0/24 -j RETURN") - run_su_command(f"iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345") - # run_su_command(f"iptables -t nat -L -nv") - # run_su_command(f"iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to 12345") - # run_su_command(f"iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to 12345") - elif proxy_type.startswith("socks"): - run_su_command(f"iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner {current_identifier_uid} -j REDIRECT --to-ports 12345") - #run_su_command(f"iptables -t nat -A PREROUTING -i wlan0 -p tcp -j REDIRECT --to-ports 12345") - info(f"proxy {proxy} OK") - else: - warn(f"Cannot set proxy {proxy}") - return -if not os.path.exists('.cache'): - os.makedirs('.cache') - -def open_or_create_db(): - global current_identifier_cache_db - if current_identifier_cache_db: - return current_identifier_cache_db - db_path=f'.cache/{current_identifier}_{current_identifier_version}_class_methods.db' - conn = sqlite3.connect(db_path, check_same_thread=False) - current_identifier_cache_db = conn - cursor = conn.cursor() - # 检查是否存在 app_methods 表 - cursor.execute(''' - SELECT name FROM sqlite_master - WHERE type='table' AND name='app_methods' - ''') - table_exists = cursor.fetchone() - if table_exists: - cursor.execute("PRAGMA table_info(app_methods)") - columns = [row[1] for row in cursor.fetchall()] - if 'filename' not in columns: - cursor.execute("DROP TABLE IF EXISTS app_methods") - conn.commit() - table_exists = False - if not table_exists: - # 创建表 - cursor.execute(''' - CREATE TABLE app_methods ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - filename TEXT NOT NULL, - class_package_name TEXT NOT NULL, - class_name TEXT NOT NULL, - method_name TEXT NOT NULL, - readable_proto_list TEXT - ) - ''') - # 创建索引 - cursor.execute('CREATE INDEX idx_class_package_prefix ON app_methods(class_package_name)') - cursor.execute('CREATE INDEX idx_class_name ON app_methods(class_name)') - conn.commit() - return current_identifier_cache_db - -def insert_if_not_exists(cursor, filename, class_package_name, class_name, - method_name, readable_proto_list): - try: - # 查询是否存在相同 class_package_name 和 class_name 的记录 - cursor.execute(''' - SELECT 1 FROM app_methods - WHERE class_package_name = ? AND class_name = ? - LIMIT 1 - ''', (class_package_name, class_name)) - exists = cursor.fetchone() - if not exists: - cursor.execute(''' - INSERT INTO app_methods ( - filename, class_package_name, class_name, - method_name, readable_proto_list - ) VALUES (?, ?, ?, ?, ?) - ''', ( - filename, class_package_name, class_name, - method_name, readable_proto_list - )) - current_identifier_cache_db.commit() - except sqlite3.DatabaseError as e: - pass - return exists - -def ensure_readonly_copy_fresh(): - """ - 确保只读数据库副本是最新的,如果过旧或主库有更新则拷贝。 - 设置 current_identifier_cache_readonly_db 为连接对象。 - """ - global current_identifier_cache_readonly_db # 注意声明为全局变量 - base_path = f'.cache/{current_identifier}_{current_identifier_version}_class_methods.db' - readonly_path = f'.cache/{current_identifier}_{current_identifier_version}_tmp_readonly_class_methods.db' - # 主库不存在直接报错 - if not os.path.exists(base_path): - raise FileNotFoundError(f"主数据库不存在: {base_path}") - now = time.time() - # 如果只读文件不存在,则直接拷贝 - if not os.path.exists(readonly_path): - shutil.copy2(base_path, readonly_path) - else: - readonly_ctime = os.path.getctime(readonly_path) - # 如果只读副本距现在小于30秒,则不更新 - if now - readonly_ctime < 30: - pass # 不拷贝 - else: - base_ctime = os.path.getctime(base_path) - if base_ctime > readonly_ctime: - shutil.copy2(base_path, readonly_path) - - # 连接(无论是否拷贝,都会使用只读副本连接) - readonly_uri = f'file:{readonly_path}?mode=ro' - current_identifier_cache_readonly_db = sqlite3.connect(readonly_uri, uri=True, check_same_thread=False) - - -def count_methods_by_app_version(): - ensure_readonly_copy_fresh() - cursor = current_identifier_cache_readonly_db.cursor() - cursor.execute('SELECT COUNT(*) FROM app_methods') - count = cursor.fetchone()[0] - return count - -def query_class_name_by_prefix(class_name_prefix, class_name, limit=15): - # 拷贝最新主库文件(如果需要) - ensure_readonly_copy_fresh() - cursor = current_identifier_cache_readonly_db.cursor() - if class_name and not "." in class_name: - if class_name_prefix: - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_name = ? AND class_package_name LIKE ? - LIMIT ? - ''', (class_name, f'%{class_name_prefix}%', limit)) - #print("1") - results = cursor.fetchall() - if results: - return results - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_name LIKE ? AND class_package_name LIKE ? - LIMIT ? - ''', (f'{class_name}%', f'%{class_name_prefix}%', limit)) - #print("2") - results = cursor.fetchall() - if results: - return results - else: - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_name = ? - LIMIT ? - ''', (class_name, limit)) - #print("3") - results = cursor.fetchall() - if results: - return results - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_name LIKE ? - LIMIT ? - ''', (f'{class_name}%', limit)) - #print("4") - results = cursor.fetchall() - if results: - return results - return [] - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_package_name LIKE ? - LIMIT ? - ''', (f'{class_name_prefix}%', limit)) - #print("5") - results = cursor.fetchall() - if results: - return results - cursor.execute(''' - SELECT class_package_name, class_name, readable_proto_list FROM app_methods - WHERE class_package_name LIKE ? - LIMIT ? - ''', (f'%{class_name_prefix}%', limit)) - #print("6") - results = cursor.fetchall() - if results: - return results - if not "." in class_name_prefix: - return [] - class_name_prefix, class_name = class_name_prefix.rsplit(".", 1) - return query_class_name_by_prefix(class_name_prefix, class_name, limit) - -def get_need_to_cache_pkg_prefix(): - results = {"okhttp3", "retrofit2", "javax.crypto", "java.security"} - try: - logging.getLogger("androguard.core.api_specific_resources").setLevel(logging.ERROR) - a = apk.APK(current_local_apk_path) - activities = a.get_activities() - # 取每个activity包名前两段 - prefixes = [] - for activity in activities: - parts = activity.split('.') - if len(parts) >= 2: - prefix = '.'.join(parts[:2]) - else: - prefix = activity - prefixes.append(prefix) - # 统计出现次数 - counter = Counter(prefixes) - # 找出现次数最多的前两个 - most_common_two = counter.most_common(2) - for prefix, count in most_common_two: - results.add(prefix) - package_name = current_identifier - parts = package_name.split('.') - results.add('.'.join(parts[:2]) if len(parts) >= 2 else package_name) - #info(f"need_to_cache_pkg_prefix:{results}") - except Exception as e: - return [] - return list(results) - -def load_dexes_to_cache(): - if not zipfile.is_zipfile(current_local_apk_path): - warn(f"{current_local_apk_path} is not a legal zip file") - return - open_or_create_db() - def process_dex(): - need_to_cache_pkg_prefix = get_need_to_cache_pkg_prefix() - with zipfile.ZipFile(current_local_apk_path, 'r') as zip_ref: - for file_info in zip_ref.infolist(): - if file_info.filename.endswith('.dex'): - with zip_ref.open(file_info.filename) as dex_file: - dex_data = dex_file.read() # 读取为 bytes - load_classes_and_methods_to_db(file_info.filename, dex_data, need_to_cache_pkg_prefix) - if current_identifier_stop_event == None or current_identifier_stop_event.is_set(): - # info("中断线程3") - break - if count_methods_by_app_version() > 300000: - break - global current_identifier_stop_event - current_identifier_stop_event = threading.Event() - t = threading.Thread(target=process_dex) - t.daemon = True - t.start() - -def load_classes_and_methods_to_db(filename, dex_bytes, need_to_cache_pkg_prefix): - if not current_identifier_cache_db: - return - if current_identifier_stop_event == None or current_identifier_stop_event.is_set(): - return - cursor = current_identifier_cache_db.cursor() - need_to_cache_pkg = tuple(need_to_cache_pkg_prefix) - obfuscated_package_cahce = set() - def is_obfuscated_package(pkg_name: str) -> bool: - if pkg_name in obfuscated_package_cahce: - return True - parts = pkg_name.split(".") - if len(parts) > 3 or (len(parts) == 1 and len(parts[0]) > 3): - return False - if len(parts) < 2 and len(parts[0]) <= 3: - obfuscated_package_cahce.add(pkg_name) - #info(f"混淆包名1:{class_package_name}") - return True - short_count = sum(1 for part in parts if len(part) <= 1) - upper_count = sum(1 for part in parts if part.isupper()) - if short_count >= len(parts) // 2: - obfuscated_package_cahce.add(pkg_name) - #info(f"混淆包名2:{class_package_name}") - return True - if upper_count >= len(parts) // 2: - obfuscated_package_cahce.add(pkg_name) - #info(f"混淆包名3:{class_package_name}") - return True - return False - def should_skip_class_package(class_package_name): - if is_obfuscated_package(class_package_name): - return False - if not class_package_name.startswith(need_to_cache_pkg): - return True - return False - loaded_count = 0 - try: - dex = dvm.DalvikVMFormat(dex_bytes) - for cls in dex.get_classes(): - if loaded_count > 2000: - #info(f"already loaded dex {filename}") - return - access_flags = cls.get_access_flags() - # (0x200 = ACC_INTERFACE)(0x400 = ACC_ABSTRACT) - if (access_flags & 0x200) or (access_flags & 0x400): - continue - if current_identifier_stop_event == None or current_identifier_stop_event.is_set(): - # info("中断线程") - return - class_name = cls.get_name().strip('L;').replace('/', '.') - temp = class_name.rsplit(".", 1) - class_package_name = temp[0] - if len(temp) != 2 or should_skip_class_package(class_package_name): - continue - class_name = temp[1] - readable_proto_list = "" - for method in cls.get_methods(): - method_name = method.get_name() - proto = method.get_descriptor() # e.g., (Ljava/lang/String;)V - # 转换描述符为更易读格式 - readable_proto = convert_descriptor_to_readable(proto) - readable_proto_list += f"|{method_name}{readable_proto}" - already_exists = insert_if_not_exists(cursor, filename, class_package_name, class_name, "", readable_proto_list) - if already_exists: - loaded_count += 1 - except Exception as e: - pass - #print(traceback.format_exc()) - #print(f"[!] Error processing {filename}: {e}") - -def convert_descriptor_to_readable(descriptor): - def type_map(d): - mapping = { - 'I': 'int', 'Z': 'boolean', 'B': 'byte', - 'S': 'short', 'J': 'long', 'F': 'float', - 'D': 'double', 'C': 'char', 'V': 'void', - } - if d.startswith('L') and d.endswith(';'): - return d[1:-1].replace('/', '.').split('.')[-1] - return mapping.get(d, d) - args, ret = descriptor.split(')') - args = args[1:] # remove opening '(' - i = 0 - parsed = [] - while i < len(args): - if args[i] == 'L': - j = i - while args[j] != ';': - j += 1 - parsed.append(type_map(args[i:j+1])) - i = j + 1 - elif args[i] in "ZBSCIJFD": - parsed.append(type_map(args[i])) - i += 1 - elif args[i] == '[': - dim = 0 - while args[i] == '[': - dim += 1 - i += 1 - if args[i] == 'L': - j = i - while args[j] != ';': - j += 1 - base = type_map(args[i:j+1]) - i = j + 1 - else: - base = type_map(args[i]) - i += 1 - parsed.append(base + '[]' * dim) - else: - i += 1 # unknown type, skip - return f"({', '.join(parsed)})" - -def push_file_to_device_with_chmod(local_file, remote_file = None): - filename = local_file.split("/")[-1] - if remote_file == None: - remote_file = f"/data/user/0/{current_identifier}/{filename}" - if not compara_and_update_file(f"{current_identifier}/{local_file}", remote_file): - raise RuntimeError(f"failed to push file to device: {remote_file}") - user_group_id = f"u0_a{(int(current_identifier_uid) - 10000)}" - run_su_command(f"chown {user_group_id}:{user_group_id} {remote_file}") - run_su_command(f"chmod 777 {remote_file}") - info(f"push file OK {remote_file}") - return remote_file -def start_web_server(jar_file:str = "", with_xposed_daemon = False): - remote_dex_file = "" - all_classes = [] - if jar_file: - dex_file = convert_jar_to_dex(jar_file) - with open(f"{current_identifier}/{dex_file}", "rb") as f: - dex = dvm.DalvikVMFormat(f.read()) - all_classes = [] - for cls in dex.get_classes(): - # 判断是否接口、抽象类、注解 - access_flags = cls.get_access_flags() - # (0x200 = ACC_INTERFACE)(0x400 = ACC_ABSTRACT)(0x2000 = ACC_ANNOTATION) - if (access_flags & 0x200) or (access_flags & 0x400) or (access_flags & 0x2000): - continue - class_name = cls.get_name()[1:-1].replace("/", ".") - all_classes.append(class_name) - if len(all_classes) == 0: - warn(f"Deploy failure. not found any class in {jar_file}") - return - remote_file = f"/data/user/0/{current_identifier}/hooker_server.dex" - remote_dex_file = push_file_to_device_with_chmod(dex_file, remote_file) - rpc_start_web_server(remote_dex_file, all_classes) -def stop_web_server(): - cmd = "curl --max-time 3 " + webserver_url + "/stop" - result = adb_device.shell(cmd) - info(result) - -def tail_android_file(filepath: str): - """ - 通过 adb 以 tail -f 方式实时读取安卓设备上的文件。 - Ctrl + C 可安全退出。 - """ - if not check_remote_file_exists(filepath): - info("There is no log yet") - return - info(f"viewloging") - cmd = ["adb", "shell", "tail", "-f", filepath] - # 启动 adb 进程 - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) +def printModuleName(target, moduleName): + online_session = None + online_script = None try: - for line in p.stdout: - if not line: - continue - line = line.rstrip() - if "[warn]" in line or "[WARN]" in line: - info(line) - elif "[error]" in line or "[ERROR]" in line: - warn(line) - else: - print(line) - except KeyboardInterrupt: - print("\nCtrl + C received, stopping tail...") + online_session,online_script,_ = attach(target); + info(online_script.exports.so(moduleName)) + except Exception: + print(traceback.format_exc()) finally: - p.terminate() # 结束 tail 进程 - try: - p.wait(timeout=2) - except subprocess.TimeoutExpired: - p.kill() - print("viewlog stopped.") + detach(online_session) -class ClassNameCompleter(Completer): - def __init__(self): - js_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".js") - } - pushable_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".dex") or filename.endswith(".so") or filename.endswith(".jpg") - } - jar_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".jar") - } - viewlog = { - "webserver": None, - } - output = adb_device.shell(f"find {current_identifier_install_path}/lib/ -type f") - self.so_files = { - path.split('/')[-1]: path - for path in output.strip().split("\n") - if path.endswith(".so") - } - self.nested_dict = { - 'help': None, - 'h': None, - 'activitys': None, - 'a': None, - 'services': None, - 's': None, - 'object': None, - 'o': None, - 'view': None, - 'v': None, - 'generatescript': None, - 'gs': None, - 'proxy': {"socks5://": None}, - 'p': {"socks5://": None}, - 'unproxy': None, - 'up': None, - 'justtrustme': None, - 'trust': None, - 'r0capture': None, - 'ls': None, - 'push': pushable_files, - 'attach': js_files, - 'frida': js_files, - 'spawn': js_files, - 'fridaf': js_files, - 'restart': None, - 'pid': None, - 'uid': None, - 'pull': None, - 'webserver': { - "start": jar_files, - "stop": None, - }, - 'viewlog': viewlog, - 'exit': None, - } - self.debug_completer = NestedCompleter.from_nested_dict(self.nested_dict) - - def update_js_files(self): - js_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".js") - } - pushable_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".dex") or filename.endswith(".so") or filename.endswith(".jpg") - } - jar_files = { - filename: None - for filename in os.listdir(current_identifier) - if filename.endswith(".jar") - } - self.nested_dict["attach"] = js_files - self.nested_dict["frida"] = js_files - self.nested_dict["spawn"] = js_files - self.nested_dict["fridaf"] = js_files - self.nested_dict["push"] = pushable_files - self.nested_dict["webserver"] = { - "start": jar_files, - "stop": None, - } - self.debug_completer = NestedCompleter.from_nested_dict(self.nested_dict) - - def get_completions(self, document, complete_event): - text = document.text_before_cursor.strip() - #print("\nget_completions:"+text) - generatescript_cmd_match = re.search(r"(generatescript|gs)\s+([^\s]+)", text) - if generatescript_cmd_match: - try: - value = generatescript_cmd_match.group(2) - max_items = 15 - count = 0 - full_class_name = None - class_name_prefix = None - class_name = None - method_prefix = None - if ":" in value: - full_class_name, method_prefix = value.split(":", 1) - else: - full_class_name = value - class_name_prefix = full_class_name - # print("\nclass_name_prefix:"+class_name_prefix) - results = query_class_name_by_prefix(class_name_prefix, class_name, limit=max_items) - # print(f"{len(results)}") - if len(results) == 0: - #info(f"query_class_name_by_prefix class_name_prefix:{class_name_prefix} class_name:{class_name}") - pass - for row in results: - count += 1 - class_package_name = row[0] - class_name = row[1] - yield Completion(f"{class_package_name}.{class_name}", start_position=-len(value)) - if count >= max_items: - return - for row in results: - class_package_name = row[0] - class_name = row[1] - readable_proto_list = row[2] - readable_proto_mehtod_list = readable_proto_list.split("|")[1:] - for readable_proto_mehtod in readable_proto_mehtod_list: - count += 1 - yield Completion(f"{class_package_name}.{class_name}:{readable_proto_mehtod}", start_position=-len(value)) - if count > max_items: - return - except Exception as e: - traceback.print_exc() - #yield Completion(f"[ERROR: {e}]", start_position=0) - pass - pull_cmd_match = re.search(r"pull\s+([^\s]+)", text) - if pull_cmd_match: - filepath = pull_cmd_match.group(1) - for so_name, so_path in self.so_files.items(): - if filepath in so_path: - yield Completion(so_path, start_position=-len(filepath)) - else: - # 其他命令提示 - for c in self.debug_completer.get_completions(document, complete_event): - yield c - -cmd_session = PromptSession() -classNameCompleter = None - -def entry_debug_mode(): - def handle_command(cmd): - cmd = cmd.strip() - if cmd.startswith("activitys") or "a" == cmd: - print_activitys() - return True - elif cmd.startswith("services") or "s" == cmd: - print_services() - return True - elif (cmd.startswith("object ") or cmd.startswith("o ")) and re.search(r"(object|o)\s+([^\s]+)", cmd): - m = re.search(r"(object|o)\s+([^\s]+)", cmd) - if m: - print_object(m.group(2)) - return True - elif (cmd.startswith("view ") or cmd.startswith("v ")) and re.search(r"(view|v)\s+([^\s]+)", cmd): - m = re.search(r"(view|v)\s+([^\s]+)", cmd) - if m: - print_view(m.group(2)) - return True - elif cmd == "ls": - list_working_dir() - classNameCompleter.update_js_files() - return True - elif cmd == "justtrustme" or cmd == "trust": - just_trust_me() - return True - elif cmd.startswith("push ") and re.search(r"push\s+([^\s]+)", cmd): - m = re.search(r"push\s+([^\s]+)", cmd) - if m: - local_file = m.group(1) - m2 = re.search(r"push\s+[^\s]+\s+([^\s]+)", cmd) - if m2 is not None: - remote_file = m2.group(1) - push_file_to_device_with_chmod(local_file, remote_file) - else: - push_file_to_device_with_chmod(local_file) - return True - elif re.search(r"webserver\s+start", cmd): - m = re.search(r"webserver\s+start\s+([^\s]+\.jar)", cmd) - if m: - jar_file = m.group(1) - start_web_server(jar_file) - else: - start_web_server() - return True - elif re.search(r"webserver\s+stop", cmd): - m = re.search(r"webserver\s+stop\s+([^\d]+)", cmd) - if m: - port = m.group(1) - stop_web_server(int(port)) - else: - stop_web_server() - return True - elif cmd.startswith("viewlog") or re.search(r"viewlog\s+([^\s]+)", cmd): - m = re.search(r"webserver\s+([^\s]+)", cmd) - if m: - device_log_file = m.group(1) - if device_log_file == "webserver": - tail_android_file("/sdcard/webserver.log") - else: - tail_android_file(device_log_file) - else: - tail_android_file("/sdcard/webserver.log") - return True - elif cmd == "r0capture": - r0capture() - return True - elif (cmd.startswith("proxy ") or cmd.startswith("p ")) and re.search(r"(proxy|p)\s+([^\s]+)", cmd): - m = re.search(r"(proxy|p)\s+([^\s]+)", cmd) - if m: - set_proxy(m.group(2)) - return True - elif cmd == "unproxy" or cmd == "up": - un_proxy() - info("un_proxy OK") - return True - elif (cmd.startswith("attach ") or cmd.startswith("frida ")) and re.search(r"(attach|frida)\s+([^\s]+\.js)", cmd): - m = re.search(r"(attach|frida)\s+([^\s]+)", cmd) - if m: - execute_script(m.group(2), False) - return True - elif (cmd.startswith("spawn ") or cmd.startswith("fridaf ")) and re.search(r"(spawn|fridaf)\s+([^\s]+\.js)", cmd): - m = re.search(r"(spawn|fridaf)\s+([^\s]+)", cmd) - if m: - execute_script(m.group(2), True) - return True - elif cmd == "restart": - restart_app(current_identifier) - return True - elif cmd == "pid": - info(current_identifier_pid) - return True - elif cmd == "uid": - info(current_identifier_uid) - return True - elif cmd.startswith("pull ") and re.search(r"pull\s+([^\s]+)", cmd): - m = re.search(r"pull\s+([^\s]+)", cmd) - if m: - path = m.group(1) - filename = path.split('/')[-1] - pull_file_to_local(path, f"{current_identifier}/{filename}", True) - return True - elif (cmd.startswith("generatescript ") or cmd.startswith("gs ")) and re.search(r"(generatescript|gs)\s+([^\s]+)", cmd): - m = re.search(r"(generatescript|gs)\s+([^\s]+)", cmd) - if m: - info("Generating frida script, please wait for a few seconds") - hook_js(m.group(2), None) - classNameCompleter.update_js_files() - else: - warn(f"Can not parse class and method: {cmd}") - return True - return False - help_msg = [ - ("h, help", "show this help message"), - ("a, activitys", "show the activity stack"), - ("s, services", "show the service stack"), - ("o, object [object_id]", "show object info by object_id or classname."), - ("v, view [view_id]", "show view info by view_id of view"), - ("gs, generatescript [class_name:method_name]", "specify the class name and method name to generate a frida hook java script file. For example: generatescript okhttp3.Request$Builder:addHeader"), - ("p, proxy [socks5_proxy_server]", "set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998"), - ("up, unproxy", "remove socks5 proxy for this app"), - ("trust, justtrustme", "quickly spawn just_trust_me.js script to kill all ssl pinning"), - ("r0capture", "quickly spawn r0capture.js script to capture ssl/tls packages"), - ("ls", "list all the frida scripts of the current app"), - ("attach, frida [script_file_name]", "quickly execute a frida script, similar to executing the command \"frida -U com.example.app -l xxx.js\". For example: attach url.js"), - ("spawn, fridaf [script_file_name]", "quickly spawn a frida script, similar to executing the command \"frida -U -f -n com.example.app -l xxx.js\". For example: spawn just_trust_me.js"), - ("restart", "restart this app"), - ("pid", "get pid of this app main process"), - ("uid", "get uid of this app"), - ("pull", "quickly pull a file to the local application's working directory with a filepath or so filename. For example: pull libmsaoaidsec.so"), - ("push", - "quickly push a file to mobile storage with specify path. eg: push example-patch.dex"), - ("webserver [start/stop] [controller.jar/8080]", - "quickly start or atop a webserver with a jar file developed by radar4hooker eg: webserver start"), - ("exit", "return to the previous level"), - ] - def print_help_msg(): - GREEN = "\033[32m" - YELLOW = "\033[33m" - RESET = "\033[0m" - # 获取终端宽度,默认宽度 80 - term_width = shutil.get_terminal_size((80, 20)).columns - max_cmd_len = max(len(cmd) for cmd, _ in help_msg) + 2 - for cmd, desc in help_msg: - cmd_part = f"{GREEN}{cmd.ljust(max_cmd_len)}{RESET}" - desc_lines = textwrap.wrap(desc, width=term_width - max_cmd_len) - if desc_lines: - print(cmd_part + f"{YELLOW}{desc_lines[0]}{RESET}") - for line in desc_lines[1:]: - print(" " * max_cmd_len + f"{YELLOW}{line}{RESET}") - else: - print(cmd_part) - hooker_cmd = "" - list_working_dir() - classNameCompleter = ClassNameCompleter() - while True: - try: - hooker_cmd = cmd_session.prompt(f'{current_identifier_name} > ', completer=classNameCompleter) - hooker_cmd = hooker_cmd.strip() - if hooker_cmd == 'exit' or hooker_cmd == 'quit': - break - if hooker_cmd == 'h' or hooker_cmd == 'help': - print_help_msg() - continue - is_handled = handle_command(hooker_cmd) - if not is_handled and hooker_cmd: - info(f"hooker command not found: {hooker_cmd} Please enter \"help\" + Enter to view the help information") - continue - elif not hooker_cmd: - continue - except (EOFError, KeyboardInterrupt): - break - - - -def pad_display(text, width): - """按显示宽度对齐文本""" - text = str(text) - padding = width - wcswidth(text) - return text + ' ' * max(padding, 0) - -def list_third_party_apps(): - identifier_list = [] - apps = enumerate_applications_adbutils(False, True) - print(f"{pad_display('PID', 6)}\t{pad_display('APP', 20)}\t{pad_display('IDENTIFIER', 35)}\tEXIST_REVERSE_DIRECTORY") - for app in sorted(apps, key=lambda x: x.pid or 0): - if app.pid is not None: # 只列出运行中的 - reverse_directory_exist = os.path.isdir(app.identifier) - print(f"{pad_display(app.pid, 6)}\t{pad_display(app.name, 20)}\t{pad_display(app.identifier, 35)}\t{'✅' if reverse_directory_exist else '❌'}") - identifier_list.append(app.identifier) - return identifier_list - -def upgrade(): - info("Upgrading hooker") - repo_url = "https://github.com/CreditTone/hooker.git" - upgrade_dir = "./.upgrade_hooker" - if os.path.exists(upgrade_dir): - # 目录存在,执行 git pull - try: - repo = Repo(upgrade_dir) - origin = repo.remotes.origin - origin.pull() - info("Repository updated with 'git pull'.") - except Exception as e: - info(f"Failed to update repository: {e}") - else: - # 目录不存在,执行 git clone - Repo.clone_from(repo_url, upgrade_dir) - info("Repository cloned.") - def copy_if_different(a: str, b: str): - """ - 如果文件 b 不存在,或者 a 和 b 内容不同, - 则把 a 覆盖复制到 b。 - """ - # 如果 b 不存在,直接复制 - if not os.path.exists(b): - shutil.copy2(a, b) - info(f"Updating {b}") - return - # 如果 a 和 b 内容相同,不做操作 - if filecmp.cmp(a, b, shallow=False): - #info(f"a 和 b 内容相同,不做操作 {a} {b}") - return - # 内容不同,复制 - shutil.copy2(a, b) - info(f"Updating {b}") - def update_dir_files(remote_dir, local_dir): - for root, dirs, files in os.walk(remote_dir): - for file in files: - file_path = os.path.join(root, file) - copy_if_different(file_path, f"{local_dir}/{file}") - update_dir_files(f"{upgrade_dir}/js", "js") - update_dir_files(f"{upgrade_dir}/mobile-deploy", "mobile-deploy") - copy_if_different(f"{upgrade_dir}/hooker.py", "hooker.py") - copy_if_different(f"{upgrade_dir}/README.md", "README.md") - copy_if_different(f"{upgrade_dir}/README_EN.md", "README_EN.md") - shutil.rmtree(upgrade_dir) - info('Please restart hooker') - sys.exit(2); +if __name__ == '__main__': + try: + opts, args = getopt.getopt(sys.argv[1:], "hp:x:a:b:c:d:v:s:t:l:e:j:k:l:g:o:m:",[]) + except getopt.GetoptError: + sys.exit(2); + #这个packageName可以是进程名也可以是进程号 + packageName = None + e = None + findclassesClassRegex = None + findclasses2ClassName = None + JhookLine = None + KhookLine = None + LhookLine = None + genarateEnv = False + out = None + activity = False + service = False + objectId = None + object2ExplainObjectId = None + viewId = None + moduleName = False + #print(opts) + for op, value in opts: + if op in ("-p", "--package"): + packageName = value + elif op in ("-s", "--scan"): + findclassesClassRegex = value + elif op in ("-t"): + findclasses2ClassName = value + elif op in ("-e", "--exist"): + e = value + elif op in ("-j", "--hookjs"): + JhookLine = value + elif op in ("-k", "--hookstr"): + KhookLine = value + elif op in ("-l"): + LhookLine = value + elif op in ("-g"): + genarateEnv = True; + elif op in ("-o", "--out"): + out = value; + elif op in ("-a"): + activity = True; + elif op in ("-b"): + service = True; + elif op in ("-c"): + objectId = value; + elif op in ("-d"): + object2ExplainObjectId = value; + elif op in ("-v"): + viewId = value; + elif op in ("-m"): + moduleName = value; + if packageName == None: + warn("packageName is none") + sys.exit(2); -if len(sys.argv) > 1: - arg = sys.argv[1] - if arg == "upgrade": - upgrade() + #初始化应用目录 + if genarateEnv and packageName: + run_env.init(packageName) + onlyCheckHookingEnverment(packageName) -while True: - try: - info("hooker Let's enjoy reverse engineering together") - info("-----------------------------------------------------------------------------------------------") - first_command_list = list_third_party_apps() - first_command_list.append("exit") - first_command_list.append("quit") - first_command_list.append("upgrade") - print("Please enter the identifier that needs to be reversed") - identifier = cmd_session.prompt('hooker(Identifier): ', completer=WordCompleter(first_command_list, ignore_case=False, match_middle=True, WORD=True)) - identifier = identifier.strip() - if identifier == 'exit' or identifier == 'exit()' or identifier == 'quit': - info('ByeBye!') - sys.exit(2); - break - if identifier == 'upgrade': - upgrade() - break - if identifier not in first_command_list: - warn("The application does not exist. Please enter an existing application") - continue - current_identifier = identifier - current_identifier_pid, current_identifier_name, current_identifier_version, current_identifier_install_path, current_identifier_install_apkfilename, current_identifier_uid = ensure_app_in_foreground(current_identifier) - if not os.path.isdir(identifier): - create_working_dir_enverment() - else: - init_working_dir_enverment() - load_dexes_to_cache() - check_dependency_files() - info(f"current working directory: hooker/{current_identifier}") - entry_debug_mode() - # 从debug模式跳出来 - current_identifier = None - current_identifier_name = None - current_identifier_version = None - current_identifier_pid = None - current_identifier_install_path = None - current_identifier_uid = None - current_local_apk_path = None - current_identifier_cache_db = None - current_identifier_cache_readonly_db = None - if current_identifier_stop_event: - current_identifier_stop_event.set() - # current_identifier_stop_event = None - except (EOFError, KeyboardInterrupt): + if e != None: + existsClass(packageName, e) + elif findclassesClassRegex: + findclasses(packageName, findclassesClassRegex) + elif findclasses2ClassName: + findclasses2(packageName, findclasses2ClassName) + elif JhookLine: + hookJs(packageName, JhookLine, out) + elif KhookLine != None: + hookStr(packageName, KhookLine) + elif LhookLine != None: + hookParma(packageName, LhookLine) + elif activity: + printActivitys(packageName) + elif service: + printServices(packageName) + elif objectId: + printObject(packageName, objectId) + elif object2ExplainObjectId: + object2Explain(packageName, object2ExplainObjectId) + elif viewId: + printView(packageName, viewId) + elif moduleName: + printModuleName(packageName, moduleName) + elif not genarateEnv: + warn(opts) sys.exit(2); diff --git a/js/_hook_js_enhance.js b/js/_hook_js_enhance.js deleted file mode 100644 index 3592426..0000000 --- a/js/_hook_js_enhance.js +++ /dev/null @@ -1,173 +0,0 @@ -function check_load_dex(className, dexfile) { - Java.perform(function() { - if (!classExists(className)) { - Java.openClassFile(dexfile).load(); - //console.log("load " + dexfile); - } - }); -}; - -function class_exists(className) { - var exists = false; - try { - var clz = Java.use(className); - exists = true; - } catch(err) { - //console.log(err); - } - return exists; -}; - -function get_class_name(obj) { - if (obj.getClass) { - return obj.getClass().getName(); - } - var javaObject = Java.use("java.lang.Object"); - return Java.cast(obj, javaObject).getClass().getName(); -} - -function sleep(time) { - var startTime = new Date().getTime() + parseInt(time, 10); - while(new Date().getTime() < startTime) {} -}; - -function load_radar_dexfile() { - loaded_radar_dex_flag = true; - Java.openClassFile(dexfile).load(); -}; - -function fast_to_json(javaObject) { - if (!loaded_radar_dex_flag){ - load_radar_dexfile(); - } - var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); - return JSONClz.toJSONString(javaObject); -}; - -function get_pretty_string(javaObject) { - if (!loaded_radar_dex_flag){ - load_radar_dexfile(); - } - var XPretty = Java.use("gz.util.XPretty"); - return XPretty.getPrettyString(javaObject); -}; - - -function get_object_field_object(javaObject, fieldName) { - if (!loaded_radar_dex_flag){ - load_radar_dexfile(); - } - var X = Java.use("gz.util.X"); - return X.getField(javaObject, fieldName); -}; - -function store_object(javaObject) { - if (!loaded_radar_dex_flag){ - load_radar_dexfile(); - } - try { - var className = getClassName(javaObject); - var ObjectsStore = Java.use("gz.radar.objects.ObjectsStore"); - var objectId = ObjectsStore.storeObject(javaObject); - console.log(className + " ObjectsStoreId: " +objectId); - } catch (error) { - console.error(error); - } -}; - -// 当Okhttp3Request对象是post的时候,你读取body会消耗一次,使后面的请求不成功,这时候我们就要克隆一个新的Request -function printAndCloneOkhttp3Request(ok3ReqObj) { - var logObj = {}; - // 类名 - logObj.class = ok3ReqObj.getClass().getName(); - // URL - logObj.url = ok3ReqObj.url().toString(); - // 方法 - logObj.method = ok3ReqObj.method(); - // 请求头 - var headers = {}; - var headerList = ok3ReqObj.headers(); - for (var i = 0; i < headerList.size(); i++) { - headers[headerList.name(i)] = headerList.value(i); - } - logObj.headers = headers; - // Tag - var tag = ok3ReqObj.tag(); - logObj.tag = tag ? tag.toString() : null; - // 请求体克隆 - var body = ok3ReqObj.body(); - var newRequest = null; - var bodyContent = null; - if (body) { - var BufferClz = Java.use("okio.Buffer"); - var buffer = BufferClz.$new(); - body.writeTo(buffer); // 第一次读取到流 - bodyContent = buffer.readUtf8(); // 保存内容 - var RequestBodyClz = Java.use("okhttp3.RequestBody"); - var newBody = RequestBodyClz.create(body.contentType(), bodyContent); - // 克隆新请求体 - newRequest = ok3ReqObj.newBuilder() - .method(ok3ReqObj.method(), newBody) - .build(); - } else { - newRequest = ok3ReqObj.newBuilder().build(); - } - logObj.body = bodyContent; - // 打印 JSON 格式 - console.log(JSON.stringify(logObj, null, 4)); - return newRequest; -} - -// Response读取body会消耗一次,使后面的程序读取不成功,这时候我们就要克隆一个新的Response -function printAndCloneOkhttp3Response(ok3ResObj) { - // 构建 JSON 数据 - var result = { - request: {}, - response: {} - }; - - // 获取 Request 信息 - var request = ok3ResObj.request(); - result.request.url = request.url().toString(); - result.request.method = request.method(); - - // 请求头 - var reqHeaders = request.headers(); - var reqHeadersJson = {}; - for (var i = 0; i < reqHeaders.size(); i++) { - reqHeadersJson[reqHeaders.name(i)] = reqHeaders.value(i); - } - result.request.headers = reqHeadersJson; - - // 获取 Response 信息 - result.response.statusCode = ok3ResObj.code(); - - var resHeaders = ok3ResObj.headers(); - var resHeadersJson = {}; - for (var i = 0; i < resHeaders.size(); i++) { - resHeadersJson[resHeaders.name(i)] = resHeaders.value(i); - } - result.response.headers = resHeadersJson; - var newOk3ResObj = ok3ResObj; - // 读取 Response Body - var body = ok3ResObj.body(); - if (body) { - try { - var bodyStr = body.string(); - result.response.body = bodyStr; - - // 重新封装 Body 防止内容被消耗 - var newBody = Java.use("okhttp3.ResponseBody").create(body.contentType(), Java.use("java.lang.String").$new(bodyStr)); - newOk3ResObj = ok3ResObj.newBuilder().body(newBody).build(); - } catch (e) { - result.response.body = "[!] Failed to read body: " + e; - } - } else { - result.response.body = "[!] No body"; - } - - // 将 JSON 数据格式化输出 - console.log(JSON.stringify(result, null, 4)); - return newOk3ResObj -} - diff --git a/js/_hook_js_prepare.js b/js/_hook_js_prepare.js deleted file mode 100644 index e65b494..0000000 --- a/js/_hook_js_prepare.js +++ /dev/null @@ -1,28 +0,0 @@ -var loaded_radar_dex_flag = false; -var dexfile = "/data/local/tmp/radar.dex"; - -function newMethodBeat(text, executor) { - var threadClz = Java.use("java.lang.Thread"); - var androidLogClz = Java.use("android.util.Log"); - var exceptionClz = Java.use("java.lang.Exception"); - var processClz = Java.use("android.os.Process"); - var currentThread = threadClz.currentThread(); - var beat = new Object(); - beat.invokeId = Math.random().toString(36).slice( - 8); - beat.executor = executor; - beat.myPid = processClz.myPid(); - beat.threadId = currentThread.getId(); - beat.threadName = currentThread.getName(); - beat.text = text; - beat.startTime = new Date().getTime(); - beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); - return beat; -}; - -function printBeat(beat) { - var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); - str += beat.text + "\n"; - str += beat.stackInfo; - str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); - console.log(str); -}; diff --git a/js/_hook_js_warp.js b/js/_hook_js_warp.js deleted file mode 100644 index 964d3b6..0000000 --- a/js/_hook_js_warp.js +++ /dev/null @@ -1,9 +0,0 @@ -rpc.exports = { - cleanup: function () { - // 清理所有拦截器 - Interceptor.detachAll(); - // 如果你设置了定时器或 Stalker,也可以清理 - Stalker.unfollow(); - // clearInterval(...); - } -}; \ No newline at end of file diff --git a/js/activity_events.js b/js/activity_events.js index 4cccbdb..acdf775 100644 --- a/js/activity_events.js +++ b/js/activity_events.js @@ -112,7 +112,7 @@ function fastTojson(javaObject) { return JSONClz.toJSONString(javaObject); }; -loadDexfile('/data/local/tmp/radar.dex'); +loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); Java.perform(function() { var radarAndroidClz = Java.use("gz.radar.Android"); diff --git a/js/android_ui.js b/js/android_ui.js index faf7413..8839d8c 100644 --- a/js/android_ui.js +++ b/js/android_ui.js @@ -12,7 +12,7 @@ function checkLoadDex(className, dexfile) { } }); }; -loadDexfile('/data/local/tmp/radar.dex'); +loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); function classExists(className) { var exists = false; try { diff --git a/js/apk_shell_scanner.js b/js/apk_shell_scanner.js deleted file mode 100644 index 4ad72cd..0000000 --- a/js/apk_shell_scanner.js +++ /dev/null @@ -1 +0,0 @@ -var _0x2416=['EcKcfT0JesKXRhNuZg==','57yk5pis5pig55m1','wrPDqjw=','wr8hZWXCg8Kxw5EgRcOsGyzCrcKnw43CrlfChcK1NcKiworChg==','wrHDoivCiw/CocKWwr9XPB7CqV7CqG/DsRxVHsKHwpQ=','6Iaf6K+b5byV5aye5YWc','dMKBPyfDmsOS','I1jDpMKe','PsOhwpZCKWbDr0zCo8KpLGjCpBHDih12NQ==','BnrDr8KBCsOMwrjDkSPCqsK6ZcO0Eyt5wpbDjBnCnQXDtsOww6zDgBPDh8K4','XUPDjg==','d0PDssKB','w7EGw4PDl0QHw5U=','ElpHw4UowrZCPVLCiMKOwok=','5YSv57i95ay45YeB','5qOz5qK55YW+6Lay54iF','6YOc5LmB55qn','DcKYfC8vZsKe','6Zux6YeR6IKo5a+q5YS+','5YSW57u75a645Yah','woXDgsKG','wpdhVcK7ASnDtUYXXcOGw4rDucKoU8K3JMOvwrhMdcO6wpx8w4TChA==','wqbDmmk=','YsK4w58ENVglw6llCMKcTcKRRMKQE1tGUsOFUw==','NS/Ct2Now4XCvMKgw4bDhDlES8KFw6LDgnbCk2hpw6DDnMOnwqvDhD0=','F8KceChufcKcDSlUZsOqVsKxcnohw6XCl8OTdcK/MmnCncK6w70=','YsK4w58LJEUs','wpUiw44h','OcOxw78=','eMO6YA==','54u75YiB5a2M','54iw5Ymc5ayU5L2V5Lmk54q0','U3/Dsg==','w7UQw7TDjVcFw5Nbw5nChcKm','wr/DksOv','5qGj5qGp5YeR6Lad54u3','6Zm66YaJ6IGK5a6o5YW8'];var _0x3253=function(_0x2416b7,_0x325317){_0x2416b7=_0x2416b7-0x0;var _0xafb0b2=_0x2416[_0x2416b7];if(_0x3253['KPSxjl']===undefined){(function(){var _0x440652;try{var _0xc387cb=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x440652=_0xc387cb();}catch(_0x4e73bc){_0x440652=window;}var _0x2739de='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x440652['atob']||(_0x440652['atob']=function(_0x1b2cdd){var _0x187523=String(_0x1b2cdd)['replace'](/=+$/,'');var _0x2a17c2='';for(var _0x58347b=0x0,_0x7f0eeb,_0x8a731c,_0x379e67=0x0;_0x8a731c=_0x187523['charAt'](_0x379e67++);~_0x8a731c&&(_0x7f0eeb=_0x58347b%0x4?_0x7f0eeb*0x40+_0x8a731c:_0x8a731c,_0x58347b++%0x4)?_0x2a17c2+=String['fromCharCode'](0xff&_0x7f0eeb>>(-0x2*_0x58347b&0x6)):0x0){_0x8a731c=_0x2739de['indexOf'](_0x8a731c);}return _0x2a17c2;});}());var _0x3185f3=function(_0x5d3982,_0x39f71d){var _0x8d472e=[],_0x2f8a41=0x0,_0x4f410c,_0x47db3d='',_0x5554d7='';_0x5d3982=atob(_0x5d3982);for(var _0x172da9=0x0,_0x48d7d6=_0x5d3982['length'];_0x172da9<_0x48d7d6;_0x172da9++){_0x5554d7+='%'+('00'+_0x5d3982['charCodeAt'](_0x172da9)['toString'](0x10))['slice'](-0x2);}_0x5d3982=decodeURIComponent(_0x5554d7);var _0x5430f4;for(_0x5430f4=0x0;_0x5430f4<0x100;_0x5430f4++){_0x8d472e[_0x5430f4]=_0x5430f4;}for(_0x5430f4=0x0;_0x5430f4<0x100;_0x5430f4++){_0x2f8a41=(_0x2f8a41+_0x8d472e[_0x5430f4]+_0x39f71d['charCodeAt'](_0x5430f4%_0x39f71d['length']))%0x100;_0x4f410c=_0x8d472e[_0x5430f4];_0x8d472e[_0x5430f4]=_0x8d472e[_0x2f8a41];_0x8d472e[_0x2f8a41]=_0x4f410c;}_0x5430f4=0x0;_0x2f8a41=0x0;for(var _0x10a532=0x0;_0x10a532<_0x5d3982['length'];_0x10a532++){_0x5430f4=(_0x5430f4+0x1)%0x100;_0x2f8a41=(_0x2f8a41+_0x8d472e[_0x5430f4])%0x100;_0x4f410c=_0x8d472e[_0x5430f4];_0x8d472e[_0x5430f4]=_0x8d472e[_0x2f8a41];_0x8d472e[_0x2f8a41]=_0x4f410c;_0x47db3d+=String['fromCharCode'](_0x5d3982['charCodeAt'](_0x10a532)^_0x8d472e[(_0x8d472e[_0x5430f4]+_0x8d472e[_0x2f8a41])%0x100]);}return _0x47db3d;};_0x3253['EDlNQl']=_0x3185f3;_0x3253['pEIUxe']={};_0x3253['KPSxjl']=!![];}var _0x9d8be2=_0x3253['pEIUxe'][_0x2416b7];if(_0x9d8be2===undefined){if(_0x3253['MPftEf']===undefined){_0x3253['MPftEf']=!![];}_0xafb0b2=_0x3253['EDlNQl'](_0xafb0b2,_0x325317);_0x3253['pEIUxe'][_0x2416b7]=_0xafb0b2;}else{_0xafb0b2=_0x9d8be2;}return _0xafb0b2;};Java[_0x3253('0x11','Hgtu')](function(){var _0x219a2c={'libchaosvmp.so':'娜迦','libddog.so':'娜迦','libfdog.so':'娜迦','libedog.so':'娜迦企业版','libexec.so':'腾讯','libexecmain.so':_0x3253('0x1e','3O0n'),'ijiami.dat':'爱加密','ijiami.ajm':_0x3253('0x1f','PmuY'),'libsecexe.so':_0x3253('0xf','!pK&'),'libsecmain.so':_0x3253('0x23','!rWc'),'libSecShell.so':'梆梆免费版','libDexHelper.so':'梆梆企业版','libDexHelper-x86.so':'梆梆企业版','libprotectClass.so':_0x3253('0xa','kEx8'),'libjiagu.so':'360','libjiagu_art.so':'360','libjiagu_x86.so':'360','libegis.so':'通付盾','libNSaferOnly.so':_0x3253('0x10','VOC('),'libnqshield.so':'网秦','libbaiduprotect.so':'百度','aliprotect.dat':_0x3253('0x24','3KEo'),'libsgmain.so':'阿里聚安全','libsgsecuritybody.so':_0x3253('0x12','c7@['),'libmobisec.so':'阿里聚安全','libtup.so':'腾讯','libshell.so':'腾讯','mix.dex':'腾讯','lib/armeabi/mix.dex':'腾讯','lib/armeabi/mixz.dex':'腾讯','libtosprotection.armeabi.so':_0x3253('0x5','9Gy#'),'libtosprotection.armeabi-v7a.so':'腾讯御安全','libtosprotection.x86.so':'腾讯御安全','libnesec.so':_0x3253('0x1','!pK&'),'libAPKProtect.so':'APKProtect','libkwscmm.so':'几维安全','libkwscr.so':_0x3253('0xe','bI]H'),'libkwslinker.so':_0x3253('0x13','hW$7'),'libx3g.so':'顶像科技','libapssec.so':'盛大','librsprotect.so':'瑞星'};var _0x3755c5=Java[_0x3253('0x14','7!0f')](_0x3253('0x15','hW$7'))['currentApplication']();var _0x548a32=_0x3755c5[_0x3253('0x17','^)q7')]();var _0x54b112=_0x548a32[_0x3253('0x8','Q&Jx')]();var _0x330c71=Java[_0x3253('0x2','VOC(')](_0x3253('0x3','#xV7'));var _0x55dc4b=Java[_0x3253('0x16','x1N&')](_0x3253('0x19','Hgtu'));var _0x2efb39=Java[_0x3253('0x22','3O0n')](_0x3253('0x9',']lvX'));var _0x2a7f21=Java[_0x3253('0x1d','YgT]')]('java.util.zip.ZipEntry');var _0x226403=Java['use'](_0x3253('0x4','kX7s'));function _0x4b1f81(_0x4b3071){var _0x5d549d=[];try{var _0x4443ed=_0x4b3071;var _0xfa07b=_0x226403[_0x3253('0xb','N1Vs')](_0x4443ed);var _0x424f8d=_0x330c71['$new'](_0x4443ed);var _0x36f7c0=_0x55dc4b['$new'](_0x424f8d);var _0x3d3fb5=_0x2efb39['$new'](_0x36f7c0);var _0x2c236f;while((_0x2c236f=_0x3d3fb5[_0x3253('0xd','!pK&')]())!=null){if(_0x2c236f[_0x3253('0x21',')Xj^')]()){continue;}var _0x3fd32e=_0x2c236f[_0x3253('0x1a','^)q7')]();if(_0x3fd32e['indexOf']('/')!==-0x1){_0x3fd32e=_0x3fd32e['substring'](_0x3fd32e[_0x3253('0x0','Hgtu')]('/')+0x1);}_0x5d549d[_0x3253('0x7','N1Vs')](_0x3fd32e);}_0x3d3fb5['closeEntry']();}catch(_0x1912a5){console['error'](_0x1912a5);_0x5d549d[_0x3253('0x1b','PFdr')](_0x3253('0x6','&KjG')+_0x1912a5[_0x3253('0xc',')Xj^')]);}return _0x5d549d;}var _0xfce2f8=_0x4b1f81(_0x54b112);for(const _0x1f3aff of _0xfce2f8){var _0x240280=_0x219a2c[_0x1f3aff];if(_0x240280!=null){console[_0x3253('0x20','6Gpu')](_0x3253('0x18','qtXH')+_0x240280+'}.');return;}}console[_0x3253('0x1c','rFZC')]('This\x20app\x20is\x20not\x20protected\x20or\x20uses\x20an\x20unknown\x20protection\x20scheme.');}); \ No newline at end of file diff --git a/js/base.js b/js/base.js new file mode 100644 index 0000000..1b90369 --- /dev/null +++ b/js/base.js @@ -0,0 +1,111 @@ +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; + +function checkLoadDex(className, dexfile) { + Java.perform(function() { + if (!classExists(className)) { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + } + }); +}; + +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; + +function getClassName(obj) { + if (obj.getClass) { + return obj.getClass().getName(); + } + var javaObject = Java.use("java.lang.Object"); + return Java.cast(obj, javaObject).getClass().getName(); +} + +//str1是否包含str2,str2可用正则表示 +function contains(str1, str2) { + var reg = RegExp(eval("/"+str2+"/")); + if(str1 && str1.match && str1.match(reg)){ + return true; + }else{ + return false; + } +}; + +//创建ArrayList对象用这个方法就好了 +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + +//创建HashSet对象用这个方法就好了 +function newHashSet() { + var HashSetClz = Java.use('java.util.HashSet'); + return HashSetClz.$new(); +} + +//创建HashMap对象用这个方法就好了 +function newHashMap() { + var HashMapClz = Java.use('java.util.HashMap'); + return HashMapClz.$new(); +} + +function newMethodBeat(text, executor) { + var threadClz = Java.use("java.lang.Thread"); + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var processClz = Java.use("android.os.Process"); + var currentThread = threadClz.currentThread(); + var beat = new Object(); + beat.invokeId = Math.random().toString(36).slice( - 8); + beat.executor = executor; + beat.myPid = processClz.myPid(); + beat.threadId = currentThread.getId(); + beat.threadName = currentThread.getName(); + beat.text = text; + beat.startTime = new Date().getTime(); + beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20); + return beat; +}; + +function printBeat(beat) { + var str = ("------------pid:" + beat.myPid + ",startFlag:" + beat.invokeId + ",objectHash:"+beat.executor+",thread(id:" + beat.threadId +",name:" + beat.threadName + "),timestamp:" + beat.startTime+"---------------\n"); + str += beat.text + "\n"; + str += beat.stackInfo; + str += ("------------endFlag:" + beat.invokeId + ",usedtime:" + (new Date().getTime() - beat.startTime) +"---------------\n"); + console.log(str); +}; + +var log = console.log; + +var print = console.log; + +function getBaseContext() { + var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); + var context = currentApplication.getApplicationContext(); + return context; //Java.scheduleOnMainThread(fn): +}; + +function sleep(time) { + var startTime = new Date().getTime() + parseInt(time, 10); + while(new Date().getTime() < startTime) {} +}; + +//byte数组转String,这个方法不能直接调,把方法逻辑内嵌到你的方法体。这里只是展示语法,告诉你怎么转 +function byteArrayToStringStudy() { + var bytes = null;//假如bytes是一个java的byte[] + var JavaString = Java.use("java.lang.String");//声明java类 + var result = JavaString.$new(bytes, "utf-8");//result就是java的String +} + diff --git a/js/bypass_frida_svc_detect.js b/js/bypass_frida_svc_detect.js deleted file mode 100644 index 0226a29..0000000 --- a/js/bypass_frida_svc_detect.js +++ /dev/null @@ -1,66 +0,0 @@ -# 自实现系统函数一个重要的前提就是它们都有标准的系统调用号,标准的机器码。所以我们绕过的时候也可以用同样的思路。 -# Frida的Memory API可以直接查找整个系统的内存内容,我们直接搜索对应函数的特征码,定位到之后再使用Interceptor进行Hook。(要注意每个架构对应的特征可能不一样) - -function hookSysOpen() { - let SYS_OPEN; - let SVC_INSTRUCTION_HEX; - const arch = Process.arch; - - if (arch === "arm64") { - SYS_OPEN = 56; // ARM64架构下open系统调用的编号 - SVC_INSTRUCTION_HEX = "01 00 00 D4"; // ARM64架构下svc指令的十六进制表示 - } else if (arch === "arm") { - SYS_OPEN = 5; // ARM架构下open系统调用的编号 - SVC_INSTRUCTION_HEX = "00 00 00 EF"; // ARM架构下svc指令的十六进制表示 - } else { - console.log("不支持的架构: " + arch); - return; - } - - console.log("当前架构: " + arch); - console.log("开始搜索SYS_OPEN系统调用..."); - - //系统调用指令(如svc)通常位于可执行代码段中(r-x) - Process.enumerateRanges('r-x').forEach(function(range) { - if (range.file && range.file.path && range.file.path.endsWith(".so")) { - console.log("搜索模块: " + range.file.path); - - Memory.scan(range.base, range.size, SVC_INSTRUCTION_HEX, { - onMatch: function(address) { - let sysCallNumber; - if (arch === "arm64") { - // 在ARM64中,系统调用号在svc指令之前的指令中 - sysCallNumber = address.sub(4).readU32() & 0xFFFF; - } else if (arch === "arm") { - // 在ARM中,系统调用号通常在r7寄存器中,这里我们只能近似处理 - sysCallNumber = address.sub(4).readU16() & 0xFF; - } - - if (sysCallNumber === SYS_OPEN) { - console.log("找到SYS_OPEN调用,地址: " + address); - - Interceptor.attach(address, { - onEnter: function(args) { - let fileName; - if (arch === "arm64") { - fileName = args[1].readUtf8String(); - } else if (arch === "arm") { - fileName = args[0].readUtf8String(); - } - console.log("SYS_OPEN被调用,文件名: " + fileName); - }, - onLeave: function(retval) { - console.log("SYS_OPEN返回值: " + retval); - } - }); - } - }, - onComplete: function() { - console.log("搜索完成"); - } - }); - } - }); -} - -hookSysOpen(); diff --git a/js/bypass_root_detect.js b/js/bypass_root_detect.js deleted file mode 100644 index 1619ff1..0000000 --- a/js/bypass_root_detect.js +++ /dev/null @@ -1,360 +0,0 @@ -/* -Original author: Daniele Linguaglossa -28/07/2021 - Edited by Simone Quatrini - Code amended to correctly run on the latest frida version - Added controls to exclude Magisk Manager -*/ - -Java.perform(function() { - var RootPackages = ["com.noshufou.android.su", "com.noshufou.android.su.elite", "eu.chainfire.supersu", - "com.koushikdutta.superuser", "com.thirdparty.superuser", "com.yellowes.su", "com.koushikdutta.rommanager", - "com.koushikdutta.rommanager.license", "com.dimonvideo.luckypatcher", "com.chelpus.lackypatch", - "com.ramdroid.appquarantine", "com.ramdroid.appquarantinepro", "com.devadvance.rootcloak", "com.devadvance.rootcloakplus", - "de.robv.android.xposed.installer", "com.saurik.substrate", "com.zachspong.temprootremovejb", "com.amphoras.hidemyroot", - "com.amphoras.hidemyrootadfree", "com.formyhm.hiderootPremium", "com.formyhm.hideroot", "me.phh.superuser", - "eu.chainfire.supersu.pro", "com.kingouser.com", "com.topjohnwu.magisk" - ]; - - var RootBinaries = ["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk", "magisk"]; - - var RootProperties = { - "ro.build.selinux": "1", - "ro.debuggable": "0", - "service.adb.root": "0", - "ro.secure": "1" - }; - - var RootPropertiesKeys = []; - - for (var k in RootProperties) RootPropertiesKeys.push(k); - - var PackageManager = Java.use("android.app.ApplicationPackageManager"); - - var Runtime = Java.use('java.lang.Runtime'); - - var NativeFile = Java.use('java.io.File'); - - var String = Java.use('java.lang.String'); - - var SystemProperties = Java.use('android.os.SystemProperties'); - - var BufferedReader = Java.use('java.io.BufferedReader'); - - var ProcessBuilder = Java.use('java.lang.ProcessBuilder'); - - var StringBuffer = Java.use('java.lang.StringBuffer'); - - var loaded_classes = Java.enumerateLoadedClassesSync(); - - send("Loaded " + loaded_classes.length + " classes!"); - - var useKeyInfo = false; - - var useProcessManager = false; - - send("loaded: " + loaded_classes.indexOf('java.lang.ProcessManager')); - - if (loaded_classes.indexOf('java.lang.ProcessManager') != -1) { - try { - //useProcessManager = true; - //var ProcessManager = Java.use('java.lang.ProcessManager'); - } catch (err) { - send("ProcessManager Hook failed: " + err); - } - } else { - send("ProcessManager hook not loaded"); - } - - var KeyInfo = null; - - if (loaded_classes.indexOf('android.security.keystore.KeyInfo') != -1) { - try { - //useKeyInfo = true; - //var KeyInfo = Java.use('android.security.keystore.KeyInfo'); - } catch (err) { - send("KeyInfo Hook failed: " + err); - } - } else { - send("KeyInfo hook not loaded"); - } - - PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pname, flags) { - var shouldFakePackage = (RootPackages.indexOf(pname) > -1); - if (shouldFakePackage) { - send("Bypass root check for package: " + pname); - pname = "set.package.name.to.a.fake.one.so.we.can.bypass.it"; - } - return this.getPackageInfo.overload('java.lang.String', 'int').call(this, pname, flags); - }; - - NativeFile.exists.implementation = function() { - var name = NativeFile.getName.call(this); - var shouldFakeReturn = (RootBinaries.indexOf(name) > -1); - if (shouldFakeReturn) { - send("Bypass return value for binary: " + name); - return false; - } else { - return this.exists.call(this); - } - }; - - var exec = Runtime.exec.overload('[Ljava.lang.String;'); - var exec1 = Runtime.exec.overload('java.lang.String'); - var exec2 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;'); - var exec3 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;'); - var exec4 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File'); - var exec5 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;', 'java.io.File'); - - exec5.implementation = function(cmd, env, dir) { - if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - if (cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - return exec5.call(this, cmd, env, dir); - }; - - exec4.implementation = function(cmdarr, env, file) { - for (var i = 0; i < cmdarr.length; i = i + 1) { - var tmp_cmd = cmdarr[i]; - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmdarr + " command"); - return exec1.call(this, fakeCmd); - } - - if (tmp_cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmdarr + " command"); - return exec1.call(this, fakeCmd); - } - } - return exec4.call(this, cmdarr, env, file); - }; - - exec3.implementation = function(cmdarr, envp) { - for (var i = 0; i < cmdarr.length; i = i + 1) { - var tmp_cmd = cmdarr[i]; - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmdarr + " command"); - return exec1.call(this, fakeCmd); - } - - if (tmp_cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmdarr + " command"); - return exec1.call(this, fakeCmd); - } - } - return exec3.call(this, cmdarr, envp); - }; - - exec2.implementation = function(cmd, env) { - if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - if (cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - return exec2.call(this, cmd, env); - }; - - exec.implementation = function(cmd) { - for (var i = 0; i < cmd.length; i = i + 1) { - var tmp_cmd = cmd[i]; - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id" || tmp_cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - - if (tmp_cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - } - - return exec.call(this, cmd); - }; - - exec1.implementation = function(cmd) { - if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id" || cmd == "sh") { - var fakeCmd = "grep"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - if (cmd == "su") { - var fakeCmd = "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"; - send("Bypass " + cmd + " command"); - return exec1.call(this, fakeCmd); - } - return exec1.call(this, cmd); - }; - - String.contains.implementation = function(name) { - if (name == "test-keys") { - send("Bypass test-keys check"); - return false; - } - return this.contains.call(this, name); - }; - - var get = SystemProperties.get.overload('java.lang.String'); - - get.implementation = function(name) { - if (RootPropertiesKeys.indexOf(name) != -1) { - send("Bypass " + name); - return RootProperties[name]; - } - return this.get.call(this, name); - }; - - Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { - onEnter: function(args) { - var path = Memory.readCString(args[0]); - path = path.split("/"); - var executable = path[path.length - 1]; - var shouldFakeReturn = (RootBinaries.indexOf(executable) > -1) - if (shouldFakeReturn) { - Memory.writeUtf8String(args[0], "/notexists"); - send("Bypass native fopen"); - } - }, - onLeave: function(retval) { - - } - }); - - Interceptor.attach(Module.findExportByName("libc.so", "system"), { - onEnter: function(args) { - var cmd = Memory.readCString(args[0]); - send("SYSTEM CMD: " + cmd); - if (cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1 || cmd == "id") { - send("Bypass native system: " + cmd); - Memory.writeUtf8String(args[0], "grep"); - } - if (cmd == "su") { - send("Bypass native system: " + cmd); - Memory.writeUtf8String(args[0], "justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"); - } - }, - onLeave: function(retval) { - - } - }); - - /* - - TO IMPLEMENT: - - Exec Family - - int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0); - int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]); - int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0); - int execlpe(const char *file, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]); - int execv(const char *path, char *const argv[]); - int execve(const char *path, char *const argv[], char *const envp[]); - int execvp(const char *file, char *const argv[]); - int execvpe(const char *file, char *const argv[], char *const envp[]); - - */ - - - BufferedReader.readLine.overload('boolean').implementation = function() { - var text = this.readLine.overload('boolean').call(this); - if (text === null) { - // just pass , i know it's ugly as hell but test != null won't work :( - } else { - var shouldFakeRead = (text.indexOf("ro.build.tags=test-keys") > -1); - if (shouldFakeRead) { - send("Bypass build.prop file read"); - text = text.replace("ro.build.tags=test-keys", "ro.build.tags=release-keys"); - } - } - return text; - }; - - var executeCommand = ProcessBuilder.command.overload('java.util.List'); - - ProcessBuilder.start.implementation = function() { - var cmd = this.command.call(this); - var shouldModifyCommand = false; - for (var i = 0; i < cmd.size(); i = i + 1) { - var tmp_cmd = cmd.get(i).toString(); - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd.indexOf("mount") != -1 || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd.indexOf("id") != -1) { - shouldModifyCommand = true; - } - } - if (shouldModifyCommand) { - send("Bypass ProcessBuilder " + cmd); - this.command.call(this, ["grep"]); - return this.start.call(this); - } - if (cmd.indexOf("su") != -1) { - send("Bypass ProcessBuilder " + cmd); - this.command.call(this, ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]); - return this.start.call(this); - } - - return this.start.call(this); - }; - - if (useProcessManager) { - var ProcManExec = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File', 'boolean'); - var ProcManExecVariant = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.lang.String', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'boolean'); - - ProcManExec.implementation = function(cmd, env, workdir, redirectstderr) { - var fake_cmd = cmd; - for (var i = 0; i < cmd.length; i = i + 1) { - var tmp_cmd = cmd[i]; - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id") { - var fake_cmd = ["grep"]; - send("Bypass " + cmdarr + " command"); - } - - if (tmp_cmd == "su") { - var fake_cmd = ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]; - send("Bypass " + cmdarr + " command"); - } - } - return ProcManExec.call(this, fake_cmd, env, workdir, redirectstderr); - }; - - ProcManExecVariant.implementation = function(cmd, env, directory, stdin, stdout, stderr, redirect) { - var fake_cmd = cmd; - for (var i = 0; i < cmd.length; i = i + 1) { - var tmp_cmd = cmd[i]; - if (tmp_cmd.indexOf("getprop") != -1 || tmp_cmd == "mount" || tmp_cmd.indexOf("build.prop") != -1 || tmp_cmd == "id") { - var fake_cmd = ["grep"]; - send("Bypass " + cmdarr + " command"); - } - - if (tmp_cmd == "su") { - var fake_cmd = ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]; - send("Bypass " + cmdarr + " command"); - } - } - return ProcManExecVariant.call(this, fake_cmd, env, directory, stdin, stdout, stderr, redirect); - }; - } - - if (useKeyInfo) { - KeyInfo.isInsideSecureHardware.implementation = function() { - send("Bypass isInsideSecureHardware"); - return true; - } - } - -}); diff --git a/js/bypass_vpn_detect.js b/js/bypass_vpn_detect.js deleted file mode 100644 index 850da1d..0000000 --- a/js/bypass_vpn_detect.js +++ /dev/null @@ -1,58 +0,0 @@ -function bypassVPNDetect(){ - Java.perform(function(){ - var NetworkInterface = Java.use("java.net.NetworkInterface") - NetworkInterface.getAll.implementation = function(){ - var nis = this.getAll() - console.log("call getAll function !!!") - nis.forEach(function(ni){ - if (ni.name.value.indexOf("tun0")>=0 || ni.name.value.indexOf("ppp0")>=0 || - ni.displayName.value.indexOf("tun0")>=0 || ni.displayName.value.indexOf("ppp0")>=0){ - ni.name.value = "xxxx" - ni.displayName.value = "xxxx" - } - }) - return nis - } - - var can_hook = false - var ConnectivityManager = Java.use("android.net.ConnectivityManager"); - ConnectivityManager.getNetworkInfo.overload('int').implementation = function(){ - if(arguments[0] == 17){ - can_hook = true - } - var ret = this.getNetworkInfo(arguments[0]) - return ret - } - - var NetworkInfo = Java.use("android.net.NetworkInfo") - NetworkInfo.isConnected.implementation = function(){ - let ret = this.isConnected() - if(can_hook){ - ret = false - can_hook = false - console.log("call isConnected function !!!") - } - return ret - } - - var NetworkCapabilities = Java.use("android.net.NetworkCapabilities") - NetworkCapabilities.hasTransport.implementation = function(){ - var ret = this.hasTransport(arguments[0]) - if(arguments[0] == 4){ - console.log("call hasTransport function !!!") - ret = false - } - return ret - } - - NetworkCapabilities.transportNameOf.overload('int').implementation = function(){ - var ret = this.transportNameOf(arguments[0]) - if(ret.indexOf("VPN") >= 0){ - ret = "WIFI" - } - return ret; - } - }) -} - -setImmediate(bypassVPNDetect) \ No newline at end of file diff --git a/js/find_anit_frida_so.js b/js/find_anit_frida_so.js deleted file mode 100644 index b7dd316..0000000 --- a/js/find_anit_frida_so.js +++ /dev/null @@ -1,18 +0,0 @@ -function hook_dlopen(){ - //Android8.0之后加载so通过android_dlopen_ext函数 - var android_dlopen_ext = Module.findExportByName(null,"android_dlopen_ext"); - console.log("addr_android_dlopen_ext",android_dlopen_ext); - Interceptor.attach(android_dlopen_ext,{ - onEnter:function(args){ - var pathptr = args[0]; - if(pathptr!=null && pathptr != undefined){ - var path = ptr(pathptr).readCString(); - console.log("android_dlopen_ext:",path); - } - }, - onLeave:function(retvel){ - console.log("leave!"); - } - }) -} -hook_dlopen() diff --git a/js/find_boringssl_custom_verify_func.js b/js/find_boringssl_custom_verify_func.js deleted file mode 100644 index 1767288..0000000 --- a/js/find_boringssl_custom_verify_func.js +++ /dev/null @@ -1,91 +0,0 @@ -var hasAlreadyHooked = false; - -//init_proc func start addr -var startAddr = null; - -//init_proc func end addr -var endAddr = null; - -// replace your so name eg: xxx.so -var somodule = "libttboringssl.so"; - -function hook_dlopen(){ - //Android8.0之后加载so通过android_dlopen_ext函数 - var android_dlopen_ext = Module.findExportByName(null,"android_dlopen_ext"); - //console.log("addr_android_dlopen_ext",android_dlopen_ext); - Interceptor.attach(android_dlopen_ext,{ - onEnter:function(args){ - var pathptr = args[0]; - if(pathptr!=null && pathptr != undefined){ - var path = ptr(pathptr).readCString(); - this.path = path; - if(path.indexOf(somodule)!=-1){ - //console.log("android_dlopen_ext:",path); - hook_custom_verify() - } - } - }, - onLeave:function(retvel){ - //console.log(this.path+" leave!"); - } - }) -} - -const targetLibrary = somodule; -const targetFunction = 'SSL_CTX_set_custom_verify'; -const functionOffset = 0x47FB0; // 函数偏移量 - -// 主函数 -function hook_SSL_CTX_set_custom_verify() { - // 获取模块基址 - const moduleBase = Module.findBaseAddress(targetLibrary); - if (!moduleBase) { - console.error(`[!] 无法找到模块: ${targetLibrary}`); - return; - } - - // 计算绝对地址 - const targetAddress = moduleBase.add(functionOffset); - console.log(`[+] ${targetLibrary} 基址: ${moduleBase}`); - console.log(`[+] ${targetFunction} 地址: ${targetAddress}`); - - // 挂钩函数 - Interceptor.attach(targetAddress, { - onEnter: function (args) { - console.log(`\n[+] ${targetFunction} 被调用`); - - // 打印参数 (根据函数原型可能有不同) - // SSL_CTX_set_custom_verify(SSL_CTX *ctx, int mode, SSL_custom_verify_callback callback) - console.log(` - SSL_CTX: ${args[0]}`); - console.log(` - 验证模式: ${args[1]}`); - console.log(` - 回调函数地址: ${args[2]}`); - var find_module = Process.findModuleByAddress(args[2]); - if (find_module) { - console.log("回调函数so偏移地址 |--> Module: " + find_module.name + " offset:" + ptr(args[2]).sub(find_module.base)); - } - // 保存参数以便在onLeave中使用 - this.ctx = args[0]; - this.mode = args[1].toInt32(); - this.callback = args[2]; - }, - onLeave: function (retval) { - console.log(`[+] ${targetFunction} 返回`); - console.log(` - 返回值: ${retval}`); - - // 如果需要,可以在这里修改返回值 - // retval.replace(ptr(0x1)); // 示例:强制返回1 - } - }); -} - - -function hook_custom_verify() { - // 延迟执行以确保模块已加载 - setTimeout(hook_SSL_CTX_set_custom_verify, 0); - -} -function main(){ - hook_dlopen() -} - -main() \ No newline at end of file diff --git a/js/get_device_info.js b/js/get_device_info.js deleted file mode 100644 index 896582e..0000000 --- a/js/get_device_info.js +++ /dev/null @@ -1 +0,0 @@ -var _0x41bb=['wrXDlGtO','BELDsg==','wq9DTnzDk3zDmMKPw7J4','F3tlw6nCvzs2','w5LDmAg=','wozCg0zDoMOib8KDwo7CvcKCOSjDlsK5w5cTUQ==','R8OVeHw=','w4vDhAo=','6Zyp5q+M5qKD5rSz5L6T5oar5ZiQ','Y8KdFg==','5Z6Z56Ka5pWO6L6U5ZOp6YeV5L6I5oaz5ZmB','6Ky45aSf5Z2N5Y6Nwo3CpA==','wr5Yw6sNw7t1W8KUwpvni5/mg7kV','wpnDtcOv','wozCg0zDo8O1bsKAwoTCvcK6KQjDlg==','5p2w5bKh5bq16L6nwpPCjlg=','w4bDsREjwrY=','w6TDlcOi','5ZK556ezwphowpc8FcObw4M=','w75iw44pwpdOQMKPwpcAwpUt54uz5p+Iwr3DhQ==','PFxGw5k=','dRBhwo8jwr3Dpw==','YcKuEsKIVUHovrLooKfmlaPkvZbmg4ca','w7vDm3hYZTnDmi93YStyZ8Ocw5TDncON','ZsKOWA==','w6tzZw==','5bi+55a45ZGk56WWYWs0AUM=','XsOc5YWs5qGK5LyI5oC7wrw=','5bWY6L2A6KClwpxh','wrvDgcKow59yYQ0=','wplvE8K1','AsOVeXkFYMKgWlPDtsOZwpzDrwLDoMKuw4N6w6jCjsOowoo=','AMKxw67DoA==','5p6H5aS7SErDllPnrJ3nu4gYw5k=','KT4g','wo3CiUrDtcOxY8Kb','BMOeaVsYZsK0EU7DscKOwpzDogPDvcKlw4k=','5YuH6YKE5buk5LyE5oWZ5ZuS77yD5qOW5reQ5Li86L+o5YiL6YGY5buA772p5ZKL6YWS5YuU77+u','5L+L55Sr54y/csKX','E8ObMQrDlsOaDA/Co8OFwoZveMO/QsOjTDI=','DMKQLA==','D8K6w7LDpi/ClsOkYV/CtsKVFm1+XsOv','ZhpTwq82wr3Dqg==','5p6z5bi1w6lHw6ww','BHFX','KMOXd2TDnQ==','w7vDh8Oxwp/Cg3EfwoxGw5oyTcOMwq5If8K8woRi','w53DlhstKcOcJ8OuwqBdwrtDMFV5wrMsMsKdNl8iJDdKC8Om','I1pb','5bSx5L+F55eGwpzCkQ==','FXVPw7XCog==','w4jDlgN4Yw==','woJEw7bDieW6p+WKn+WMu8OWwqI=','BHXDncKB','IsO4SUI8QMKQLWPDlsKywp3DgDjDl8KO','XgUQMsOYwqAKU8OWc1B3ZMKM','6Zuu6J+45Lqb77+W5qO25raW6K+t5aaP55i+6Keh6YG/5bmm','5L2D5oSL5ZmLwqA=','MsKLQlzDtVrCk0fDg8OmFsOhwrVODsK0wrgtcxLDpcKPU2vCiAvCvsKa','G8Knw7M=','w6zDmyE0wo/CoCwjwr1L','wqlKXUHDlHvDmMKOw7Ju','eBRjwqdgwqrDu8OrAUZgw6g3dkfDt8KVwpQqwpg=','w550BMKww4t4YyLDpsKKwo4yWQ/CiMKuwo3Dog==','D8O6MWojw4Jt','KwlF','IDUiSsOTWicRa8KaRyzChsKOVyHDocKMw5phw48Ywq9CwrobOwctwr4eXBx1w74e','6YW8572Q5pae5LqRPsOc','6L6f5Yqd5qO85re35L+85oWu5Zim','w5sxFA==','w6rDkDEUwpXCpzxkwrNBw5dDOsO5Gz91w4/DrMKVw5PCkFl/w5lIwpBIwrHCuD7CikE=','w6EZTQ==','5ZeR6YSI5L6o5oac5ZqNwoYZ','w7/DgcOaw6zCisOI5p266ZmVeQ==','AHHDmsK8w716IsOvwqU+w7M=','ejQc','wrXDlcKCw5LDkH/Dhw==','w57CnDM=','w7rDvjxdSsOuBw==','BMOBEAzDgcOdFik=','w6PCsMKxFMKDwok=','w7ALRMK3akjDpcKJI8ObAVIjc8O6wokWIsKzw6nCtMOeBQ==','wqViw73CgFDDrcKo','ccOdwozDpx/Ds8K7wr/DkcKVQg==','w6HDg8O1wpjDgmod','5Yi26IGMwqXDmMOYOsKIwpYJ','w4bDvw4=','w6lowqMUw7TDqGkTwpvCs8KUwqLClA==','w7DDlGpLJDzDn29xPwkpSMOLw4nDmcOefTTCusOnMMKBwrzCscOo','AcKULsKIDUwDBcOywpnDujLCrwl/CMO9','w5FuEQ==','AMO+NmU2w4p1f8O4VMOow4IoCCLDj8OxO8KCZxg=','5YSq57qr5LyD5oaf5Zqv772E5qKS5rSF54285aCh5Ye05b2b5bum','OcOTb0HDigpdw6nDosK5RcK9RVJmwrrDrw==','wp7ClV0=','5qyx5oKG5qCp5rS577+z6K+15q6E5ZiO','5a+a5YaN57qB5YiFwqAT','Q8Khw4PDq1TDuWx6H8KaVcOvP8K6WcO5Y2shWcOTwpJqUGs=','wrXDm8KCw5PDlA==','VsKhw4Rxwrl5SMOPw7fDjnJYZgDDpMK1ZcOAHsOcwpnDhXPDkg==','w7NrwqM=','E3vDqMKcw6trAw==','Yyge','77+D6YGG5Yuo5L2X5oGl5pek5rK86Iyz5Y23','w5/Doww=','W8O7wqU=','w7DDlsO3wpLDhHsRw5Zmw5ssccOWwptT','w7DDiMOjwozDgnEUwoxuw4QyFsO5wp5IZMKvwox7GSRfw4bCg2/DqA==','w5hsw5ks','wqVow5bCmU3DusKtwpjDt8KqBw==','Ueitn+WkuOWck+aequS/r+aCrF0=','O8OaemHDiwBJw57DtcKsUMKaWE1q','OkZZ','w5hsw58=','DknCtQ==','w7d0wovDs0DCp3vChmfCrn7Cs8Okw4FbWcO9w6fDsxUjFg==','w44+w7HCucKIasOEw4VKw71sGADCnMOSw6jDg8K4AcOUwqc=','57O55buAAsKKw48+wopscg==','KDA/YcKOVSlaPw==','5p+l5p2hHA4=','5L+555Sw5oCD5YeP5oiS5ZOO5pSn5o2hw5LDlQ==','DBwCZcORGA==','5rqt5oua5pSU6Lyc55yO6YS05Lyh5oWi5Zig772O5L+x5YiV6IGz77ys','M8Oiwp08wog5woQ=','w7d0wovDs0DCp3vChmLCqmjCvsOywo5kWcK3w53Dtz8iFMK3WkoLCcKbf8Kg','6KaY56Oz5Y+Q5Ym35LyM5oaO5Zi7','HzU6woVAw5w2Jw==','XcKAAQ==','FcOacX4P','wrZow4/CpEnDsMKlwoHDt8K1MMKuAx/CnQ==','5YGP5pat5L635oSJ5ZiJ','wr1iw5w=','w6XDjWMiw4hSL8KSw6LDmcKOf8KlXzo=','w7F/wpvDkU7CrXTDiW3CrlfCu8Orwo5xWcOr','5ZCO54uiw47CnkhWKMOKw6Vmw6XCgA==','wokWw4c=','w73DlWk=','6YWn5YiL5L2J5oa55Zip772R5qOU5rWw55285ayz6YaD5YuK5pa35ZGI','w7DDiMOjwozDgnEUwoxsw5ssTMOdwpNII8KpwoghMBFUw5/Ch2nDqSLDnkjCp0jClMOP','DBwUZcORGA==','IANWw7jCmnDDnj8/wow=','6KyO57qX54u95pyLw5PDqg==','ZBR5wrMr','JETDu8OV5qKr5b+N5pSXwq3DsQ==','wp0iw5A=','5aWL5bqA5biE5Yi35L2p5L+25oSf5Zm9','5pua5ZK857Gz57mc5bmx55aUAyU=','WjFWwpbnrIfnub/CtMKl','AMKhbXHDk33Cow==','f8KPVMKHw6l5wq0=','FsOIwqggwq0=','5Y+G5ZK3w47ChsKROMKCE8O6S8KS','HMOBJA==','BMOuMFYjw5RtV8OrRcOww4IkBxXDicOwIQ==','5ayO5YW06KGl5LiP5pWw5pycbk8=','wqbDgsKHw5XDhWs=','GsOPNRnCncOdF2DCmsObwppzb8ONQsO4Rj3Du8KiAR/DssKBw4M=','B8KaPw==','R3nDuA==','R+Kch2I=','GcK2w4c=','6I2M5ZuBHMKcw7xRbnE5','dRBhwpY8wrfDvsOgB1t0w49gdEfDhMKUwo8kwo8=','RMKmw5pm','cT4PwpdUIgjDq8OwbwDCjiQhw4obw6zDsioTwr4=','57KZ57u554qa5p2twpPDv8KXwrTDoMKNCBrDljIow6o=','GMKPw6wJScOOGMOBwqvCukHDksOIw4lEe8Oa','w7DDjXYLw6VDCcKT','A8OXMAzDlsOZMSo=','UMKBAmg5HsKjEx5jDMO2w7YowrhSwq7Cv8OeccOPOWbDucKwcAXDjMK+YRDClmLCvsKaw4x9','wqjDi8Kaw7JvcgDDp8OEBsK7wojCgMOd5Lmx5a+85ZyS','wqRJXQ==','w5gjHcK5Uw==','HsKBw70=','5Lia5ZOA5ZG7w6LChkZFLMOSwq/CoQ8TVMOc','w6BmLnvDoWo4QXo=','AMKqw4U=','w4/DqAzDhQ==','Iz0oQ8KZ','QsK8w4U=','VsKhw4Rxwrl5SMOPw7nDjSw0UgrDvMK4N8O/L8O3wp7DpE7DuW/CqA==','DkVMwpzmn7boj4blv5nmnIjpmYlpwog=','GsKkw47DqlA=','wqPDgcKJ','5bOH5peQGg==','GC86wotf','Ul9qEw==','5pSt5ZG75LyI5oee5Zmr','GMKQNsKaTFI=','w7NzRmfDrWYw','56Kz5Z+U5L665oW35ZiP77645qK55rek5Z+855KX56OS5Z6d','wpTDtMOsw7HCisOVXStLw7zDoHHChyg0YcOGw5/CtFRyCcOpw54=','5Y+h5ZWSGcKg','woXDu8Orw6jChMObXEtFw6LCqw==','wr1VXw==','w44+w7HCusKUdcOLw4dqw7NtHxvCvsO+w6nDig==','dRBhwosvwqDDncOgBlxkw6J3Q03DsMKIwok=','BnrDisKHw7xnA8K5wrIjw7xww68ywpHDh8OXRh4TwpjDjTrDsyXDilnDtRtKCsKew7ZR','6Lyl6JK/5ZeOwrnDhQ==','UMOxwrbDhTTDh8KEwp/Du8K+','w7x7wpnDoAHCp3DChkjCvnzCvMOgwp1zWMOLw6vDszU0CQ==','AsKLw6gOCMOQFA==','5pun5ZCA5YW/55Sb5LqLKMKM','5oK95Yad5a6eQMKX','w6TDiMOswpDDgm8e','woDDqcOt','5a646KO75bqN55WP5pW06YSzwrg0','wrrCpsKmA8KYwp5sUiovw5HCpRdQ','wp4ww5lSwow=','w57CgTcXwpthEcKA','dRBhwpUnwrXDncOgB0Zsw6FXdU/Dp8KDwo8=','IANWw67CimjDgzU3wqBY','w5Nmw4wJw4dsWcKIwoAIwowhw5TCisOEwooDwrNZw7c9','5ru45bqrwoHDiQ==','wrbDicKL','wqjDi8Kaw6ZlcwzDtg==','wr/DhsKBw5hv','A1DCsyjDn8K2wqfCncOJwpg7JsOwfmHCucKIw6PChg==','w7bDn3p6JDvDjyR+OytiesOHw5HDhsONZg==','w4U0w6I=','57iC5oCm5YuW6YOd5biI5Ly25oSI5ZiL776e5Y2B5o2r6YaW5Yqh5Ymo6YSz77+O','EMOCwr87wqoR','wqjDi8Kaw7dodwbDqMOUO8K6w7DDiMKJFA==','wo3Ck1bDk8OkacKcwoU=','FmdG','5Lyh55ae546cK8KY','w6fDkTI=','wofCiV8=','w7gFTw==','JD8jXsKEUSIGJcOCEDzDlcONF27Di8KHwoNlwpIP','cCkUwrt3JhbDq8O9aQ=='];var _0xb76c=function(_0x41bb9c,_0xb76cd1){_0x41bb9c=_0x41bb9c-0x0;var _0x3a044b=_0x41bb[_0x41bb9c];if(_0xb76c['AiqGzZ']===undefined){(function(){var _0x129565=function(){var _0x290030;try{_0x290030=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');')();}catch(_0x2e8c27){_0x290030=window;}return _0x290030;};var _0x5e8c9e=_0x129565();var _0x12ea3e='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x5e8c9e['atob']||(_0x5e8c9e['atob']=function(_0x26b015){var _0x16ad1e=String(_0x26b015)['replace'](/=+$/,'');var _0x5d7bb3='';for(var _0x536058=0x0,_0x211ac9,_0x467e0a,_0x12d9f0=0x0;_0x467e0a=_0x16ad1e['charAt'](_0x12d9f0++);~_0x467e0a&&(_0x211ac9=_0x536058%0x4?_0x211ac9*0x40+_0x467e0a:_0x467e0a,_0x536058++%0x4)?_0x5d7bb3+=String['fromCharCode'](0xff&_0x211ac9>>(-0x2*_0x536058&0x6)):0x0){_0x467e0a=_0x12ea3e['indexOf'](_0x467e0a);}return _0x5d7bb3;});}());var _0x33253d=function(_0x11f4c4,_0x1109da){var _0x3974e8=[],_0x478948=0x0,_0x2d2a4d,_0x430381='',_0x56c803='';_0x11f4c4=atob(_0x11f4c4);for(var _0xad2b9a=0x0,_0x1cf9dd=_0x11f4c4['length'];_0xad2b9a<_0x1cf9dd;_0xad2b9a++){_0x56c803+='%'+('00'+_0x11f4c4['charCodeAt'](_0xad2b9a)['toString'](0x10))['slice'](-0x2);}_0x11f4c4=decodeURIComponent(_0x56c803);var _0x500a42;for(_0x500a42=0x0;_0x500a42<0x100;_0x500a42++){_0x3974e8[_0x500a42]=_0x500a42;}for(_0x500a42=0x0;_0x500a42<0x100;_0x500a42++){_0x478948=(_0x478948+_0x3974e8[_0x500a42]+_0x1109da['charCodeAt'](_0x500a42%_0x1109da['length']))%0x100;_0x2d2a4d=_0x3974e8[_0x500a42];_0x3974e8[_0x500a42]=_0x3974e8[_0x478948];_0x3974e8[_0x478948]=_0x2d2a4d;}_0x500a42=0x0;_0x478948=0x0;for(var _0x30537f=0x0;_0x30537f<_0x11f4c4['length'];_0x30537f++){_0x500a42=(_0x500a42+0x1)%0x100;_0x478948=(_0x478948+_0x3974e8[_0x500a42])%0x100;_0x2d2a4d=_0x3974e8[_0x500a42];_0x3974e8[_0x500a42]=_0x3974e8[_0x478948];_0x3974e8[_0x478948]=_0x2d2a4d;_0x430381+=String['fromCharCode'](_0x11f4c4['charCodeAt'](_0x30537f)^_0x3974e8[(_0x3974e8[_0x500a42]+_0x3974e8[_0x478948])%0x100]);}return _0x430381;};_0xb76c['eAzbHL']=_0x33253d;_0xb76c['DvgAsm']={};_0xb76c['AiqGzZ']=!![];}var _0x486fab=_0xb76c['DvgAsm'][_0x41bb9c];if(_0x486fab===undefined){if(_0xb76c['AJnXwf']===undefined){_0xb76c['AJnXwf']=!![];}_0x3a044b=_0xb76c['eAzbHL'](_0x3a044b,_0xb76cd1);_0xb76c['DvgAsm'][_0x41bb9c]=_0x3a044b;}else{_0x3a044b=_0x486fab;}return _0x3a044b;};var loadedXRadar=![];function loadDexfile(_0x4357f6){Java['perform'](function(){Java[_0xb76c('0x57','hass')](_0x4357f6)[_0xb76c('0x6c','Jn4R')]();});};function loadXRadarDexfile(){loadedXRadar=!![];loadDexfile(_0xb76c('0x61','1Yzc'));};loadXRadarDexfile();function ___oo_xx(_0x4e2e4a,_0x2a2825){var _0x159bf6=Java[_0xb76c('0x7','jvew')](_0xb76c('0xaf','fV&p'));return _0x159bf6['getField'](_0x4e2e4a,_0x2a2825);};var read_phone_state=![];function getBasicInfo(){Java[_0xb76c('0xc9','c!u1')](function(){var _0x50d23c=Java[_0xb76c('0xd6','%TD^')](_0xb76c('0xb4','Ol0x'))['currentApplication']()[_0xb76c('0xa4','0AcY')]();var _0x12549a='android.permission.READ_PHONE_STATE';var _0x1968c0=Java['use'](_0xb76c('0x8a','GgCx'));var _0x26621e=_0x50d23c['checkSelfPermission'](_0x12549a);if(_0x26621e===_0x1968c0['PERMISSION_GRANTED']['value']){read_phone_state=!![];}else{console['log'](_0xb76c('0xb5','KBW$')+_0x12549a+_0xb76c('0x67','LK]#'));}console[_0xb76c('0xe3','Gpjs')](_0xb76c('0x6e','*&Uh'));var _0x279ab3=Java['use']('android.os.Build');console[_0xb76c('0xdb','RzFi')](_0xb76c('0x86','LK]#')+_0x279ab3['BRAND'][_0xb76c('0x80','tWgZ')]);console['log']('制造商\x20(MANUFACTURER):\x20'+_0x279ab3['MANUFACTURER'][_0xb76c('0xb6','1Yzc')]);console['log']('型号\x20(MODEL):\x20'+_0x279ab3['MODEL'][_0xb76c('0xd0','J6uD')]);console['log']('设备\x20(DEVICE):\x20'+_0x279ab3[_0xb76c('0xdd','Wb(N')]['value']);console[_0xb76c('0xe2','r]YV')]('主板\x20(BOARD):\x20'+_0x279ab3[_0xb76c('0x96','Wb(N')][_0xb76c('0x33','jvew')]);console[_0xb76c('0x88','!3$0')]('硬件\x20(HARDWARE):\x20'+_0x279ab3['HARDWARE']['value']);console[_0xb76c('0xad','c!u1')](_0xb76c('0xae','6QjQ')+_0x279ab3['PRODUCT']['value']);console[_0xb76c('0x98','oW(5')]('指纹\x20(FINGERPRINT):\x20'+_0x279ab3[_0xb76c('0x53','rLdG')]['value']);console[_0xb76c('0x9','PdD4')]('序列号\x20(SERIAL):\x20'+_0x279ab3['SERIAL'][_0xb76c('0x62','%TD^')]);var _0x805bb2=Java['use'](_0xb76c('0xbf','V#H6'));var _0xa892ae=_0x805bb2['RELEASE'][_0xb76c('0xac','DtLk')];var _0x56bf78=_0x805bb2[_0xb76c('0x94','XHLS')]['value'];console[_0xb76c('0x30','KBW$')](_0xb76c('0xa5','hq(a')+_0xa892ae);console['log']('SDK\x20版本\x20(SDK_INT):\x20'+_0x56bf78);var _0x3a2338=Java[_0xb76c('0xb3','Ol0x')]('android.provider.Settings$Secure');var _0x39d013=Java['use'](_0xb76c('0x6b','GgCx'))[_0xb76c('0x27','oW(5')]();var _0x56f17c=_0x39d013[_0xb76c('0x74','RzFi')]();var _0xeee8f6=_0x56f17c[_0xb76c('0xda','!3$0')]();var _0x391e05=_0x3a2338['getString'](_0xeee8f6,'android_id');console['log']('Android\x20ID:\x20'+_0x391e05);if(read_phone_state){var _0x4a2e3e=Java['use']('android.telephony.TelephonyManager');var _0x2080b3=_0x56f17c['getSystemService'](_0xb76c('0xd8','LK]#'));var _0x3a9def=Java['cast'](_0x2080b3,_0x4a2e3e);var _0x5644b5=null;if(_0x56bf78>=0x1a){if(typeof _0x3a9def['getImei']===_0xb76c('0xdf','Gpjs')){_0x5644b5=_0x3a9def[_0xb76c('0x15','KgWW')]();}else{_0x5644b5='getImei()\x20不存在';}}else{if(typeof _0x3a9def[_0xb76c('0xd3','xC)^')]==='function'){_0x5644b5=_0x3a9def['getDeviceId']();}else{_0x5644b5=_0xb76c('0xaa','LK]#');}}console['log'](_0xb76c('0x79','w!FJ')+_0x5644b5);var _0x572503=_0x3a9def['getSubscriberId']();console[_0xb76c('0x71','Jn4R')](_0xb76c('0x8b','w!FJ')+_0x572503);var _0x1863a4=_0x3a9def[_0xb76c('0xd2','KgWW')]();console[_0xb76c('0x22','w!FJ')](_0xb76c('0x34','()Fr')+_0x1863a4);var _0x3263a7=_0x3a9def['getNetworkOperatorName']();console['log'](_0xb76c('0xc6','V#H6')+_0x3263a7);var _0x3ac1af=_0x3a9def['getLine1Number']();console[_0xb76c('0xb7','LK]#')]('手机号:\x20'+_0x3ac1af);}var _0x228c81=Java[_0xb76c('0x47','hIlB')]('android.app.ActivityManager');var _0x37fc49=Java[_0xb76c('0xe0','rGCE')]('android.content.Context');var _0x14ee84=Java['cast'](_0x50d23c['getSystemService'](_0x37fc49[_0xb76c('0x36','tWgZ')]['value']),_0x228c81);var _0x29a7ca=Java['use']('android.app.ActivityManager$MemoryInfo');var _0x44a639=_0x29a7ca['$new']();_0x14ee84['getMemoryInfo'](_0x44a639);var _0x3375bc=_0x44a639['totalMem'][_0xb76c('0x2d','gC^Y')]/(0x400*0x400);var _0x27a1a3=_0x44a639[_0xb76c('0x7e','k[OV')][_0xb76c('0x8e','KgWW')]/(0x400*0x400);var _0x2d92bc=_0x3375bc-_0x27a1a3;var _0x28443e=(_0x2d92bc/_0x3375bc*0x64)[_0xb76c('0xbd','fV&p')](0x2);console['log']('[内存信息]');console[_0xb76c('0x19','fV&p')](_0xb76c('0xcb','hass')+_0x3375bc['toFixed'](0x2)+'\x20MB');console['log'](_0xb76c('0x31','%TD^')+_0x2d92bc[_0xb76c('0x3','rGCE')](0x2)+'\x20MB');console['log'](_0xb76c('0x26','Jn4R')+_0x28443e+'\x20%');var _0x14cec5=Java['use'](_0xb76c('0x59','q@2W'))[_0xb76c('0x8c','xC)^')]()[_0xb76c('0xd9','hq(a')]();console['log']('[CPU\x20信息]');console[_0xb76c('0xd','V#H6')](_0xb76c('0x8f','G$5r')+_0x14cec5);var _0x16e421=Java['use']('android.os.StatFs');var _0x2233a9=Java['use']('java.io.File');var _0x185483=_0x56f17c['getFilesDir']()['getAbsolutePath']();var _0x5de7f7=_0x16e421['$new'](_0x2233a9[_0xb76c('0x0','!3$0')](_0x185483)[_0xb76c('0xde','LK]#')]());var _0xee0520=_0x5de7f7['getBlockSizeLong']();var _0x517ba5=_0x5de7f7[_0xb76c('0xc3','RzFi')]();var _0x42fa2e=_0x5de7f7['getAvailableBlocksLong']();var _0x46cacb=_0xee0520*_0x517ba5/(0x400*0x400);var _0x5878e8=_0xee0520*_0x42fa2e/(0x400*0x400);var _0x4cd558=_0x46cacb-_0x5878e8;var _0x29178f=(_0x4cd558/_0x46cacb*0x64)[_0xb76c('0x2a','KgWW')](0x2);console[_0xb76c('0xdb','RzFi')]('[存储信息]');console['log']('总存储:\x20'+_0x46cacb['toFixed'](0x2)+_0xb76c('0x87','RzFi'));console[_0xb76c('0x4b','0AcY')]('已使用:\x20'+_0x4cd558[_0xb76c('0x65','G$5r')](0x2)+'\x20MB');console[_0xb76c('0x9d','q@2W')](_0xb76c('0xe1','gC^Y')+_0x29178f+'\x20%');var _0x20adec=Java['use'](_0xb76c('0x3a','XHLS'));var _0x263d47=Java['use']('android.os.BatteryManager');var _0x3ea4fc=Java[_0xb76c('0xcd','V#H6')](_0xb76c('0xe5','w!FJ'));var _0x55d14a=_0x20adec['$new'](_0xb76c('0xa9','Y3l0'));var _0x385457=_0x56f17c['registerReceiver'](null,_0x55d14a);var _0x544c39=_0x385457['getIntExtra']('status',-0x1);var _0x2199ed=_0x544c39==0x2||_0x544c39==0x5;var _0xd294d6=_0x385457[_0xb76c('0x4a','G$5r')](_0xb76c('0x4c','%TD^'),-0x1);var _0x34265e=_0x385457['getIntExtra'](_0xb76c('0x6d','()Fr'),-0x1)/0xa;console[_0xb76c('0xe4','hIlB')]('[电池信息]');console['log'](_0xb76c('0xca','f!1Z')+(_0x2199ed?'是':'否'));console[_0xb76c('0x18','*&Uh')]('电压:\x20'+_0xd294d6+_0xb76c('0x9e','G$5r'));console[_0xb76c('0x30','KBW$')](_0xb76c('0xd5','()Fr')+_0x34265e[_0xb76c('0x52','()Fr')](0x1)+'\x20℃');});}function getSensos(){Java['perform'](function(){var _0x54960d=Java[_0xb76c('0x66','0AcY')](_0xb76c('0x58','!3$0'))[_0xb76c('0x3f','6QjQ')]();var _0x262c16=_0x54960d[_0xb76c('0x5b','o!EJ')]();var _0x3acfa3=Java[_0xb76c('0x45','DtLk')](_0xb76c('0x7c','1iFI'));var _0x519548=_0x262c16['getSystemService'](_0xb76c('0xbc','q@2W'));var _0x5bbe6f=Java[_0xb76c('0x1','y*D9')]('android.hardware.Sensor');var _0x182c6e=Java['use']('android.content.Context');var _0x596795=Java[_0xb76c('0xba','@#y9')](_0x262c16['getSystemService'](_0x182c6e['SENSOR_SERVICE'][_0xb76c('0x32','rGCE')]),_0x3acfa3);var _0x30ee7d=_0x596795[_0xb76c('0xe','Gpjs')](_0x5bbe6f['TYPE_ALL'][_0xb76c('0x8e','KgWW')]);console['log']('sensorService:'+_0x519548);console['log']('[传感器信息]');console['log']('传感器数量:\x20'+_0x30ee7d[_0xb76c('0xa3','Ol0x')]());var _0x9860a0=_0x30ee7d['iterator']();var _0x5ec608={1:_0xb76c('0x25','o!EJ'),2:_0xb76c('0xbe','c!u1'),3:_0xb76c('0xbb','hIlB'),4:_0xb76c('0x38','DtLk'),5:_0xb76c('0x5c','rGCE'),6:'压力传感器:检测气压,估算海拔',8:'距离传感器:靠近时自动熄屏',9:_0xb76c('0x89','evJL'),10:_0xb76c('0xdc','6QjQ'),11:'旋转矢量传感器:方向感知(用于\x20AR)',15:_0xb76c('0x7a','JCfi'),16:'未校准陀螺仪:原始角速度数据',18:'步态检测:步测器',19:_0xb76c('0x5f','Wb(N'),20:_0xb76c('0xa','RzFi'),35:'未校准加速度传感器',17:_0xb76c('0x91','1Yzc'),22:_0xb76c('0x82','y*D9'),29:_0xb76c('0x8','oW(5'),30:_0xb76c('0x44','KBW$'),33171026:_0xb76c('0x7d','k[OV')};var _0x518d20=0x0;while(_0x9860a0[_0xb76c('0x40','o!EJ')]()){var _0x494da0=Java[_0xb76c('0x35','G$5r')](_0x9860a0[_0xb76c('0x20','&zBq')](),_0x5bbe6f);var _0x134493=_0x494da0['getType']();var _0x58a301=_0x5ec608[_0x134493]||'未知传感器类型';console[_0xb76c('0x71','Jn4R')](_0xb76c('0x39','rGCE')+_0x518d20);console['log'](_0xb76c('0x12','Jn4R')+_0x494da0[_0xb76c('0x7b','Wb(N')]());console[_0xb76c('0x56','evJL')]('厂商\x20\x20\x20\x20\x20:\x20'+_0x494da0['getVendor']());console['log']('类型\x20\x20\x20\x20\x20:\x20'+_0x134493+_0xb76c('0x9f','o!EJ')+_0x58a301);console['log'](_0xb76c('0x48','V#H6')+_0x494da0[_0xb76c('0x37','B6Wh')]());console[_0xb76c('0xe3','Gpjs')](_0xb76c('0x55','RzFi')+_0x494da0[_0xb76c('0xd7','LK]#')]()+'\x20mA');console['log'](_0xb76c('0x75','xC)^')+_0x494da0['getResolution']());console['log'](_0xb76c('0xa1','KBW$')+_0x494da0[_0xb76c('0x81','()Fr')]());console[_0xb76c('0x69','rLdG')](_0xb76c('0xf','oW(5')+_0x494da0['getMinDelay']()+'\x20μs');_0x518d20++;}});}function getInstalledPackages(){Java['perform'](function(){var _0x431f3f=Java[_0xb76c('0x90','J6uD')]('android.app.ActivityThread')[_0xb76c('0x99','o!EJ')]()[_0xb76c('0xd4','Jn4R')]();var _0x131e66=_0x431f3f[_0xb76c('0x85','1iFI')]();var _0x2b2873=Java[_0xb76c('0xc2','f!1Z')](_0xb76c('0x46','r]YV'));var _0x52abc7=Java['use'](_0xb76c('0xc5','G$5r'));var _0x470ef3=_0x131e66['getInstalledPackages'](0x0);var _0x95e07f=_0x470ef3[_0xb76c('0x14','KBW$')]();console[_0xb76c('0x72','hq(a')]('[应用信息]');console['log'](_0xb76c('0xce','@#y9')+_0x95e07f);for(var _0x325eab=0x0;_0x325eab<_0x95e07f;_0x325eab++){var _0x3dbb5d=_0x470ef3['get'](_0x325eab);var _0x2f8e8f=___oo_xx(_0x3dbb5d,_0xb76c('0x6a','GgCx'));var _0x50b3ad=_0x131e66['getApplicationLabel'](_0x2f8e8f);var _0x38920e=___oo_xx(_0x3dbb5d,_0xb76c('0xc1','V#H6'));var _0x1ef2ba=___oo_xx(_0x2f8e8f,_0xb76c('0xb9','k[OV'));var _0x55a12f=___oo_xx(_0x2f8e8f,'FLAG_SYSTEM');var _0x5e05e9=(_0x1ef2ba&_0x55a12f)!==0x0;console[_0xb76c('0x7f','Y3l0')]('------'+(_0x325eab+0x1)+'-------');console[_0xb76c('0x19','fV&p')](_0xb76c('0x1a','evJL')+_0x50b3ad);console[_0xb76c('0x69','rLdG')](_0xb76c('0x97','%TD^')+_0x38920e);console['log'](_0xb76c('0x92','V#H6')+(_0x5e05e9?'是':'否'));}});}function getSystemInfo(){Java[_0xb76c('0x54','GgCx')](function(){var _0x3882e5=Java[_0xb76c('0x45','DtLk')](_0xb76c('0x29','&zBq'));var _0x23a51f=Java[_0xb76c('0x64','hass')]('android.os.SystemProperties');var _0x1bf12f=Java[_0xb76c('0x47','hIlB')](_0xb76c('0x63','Ol0x'));var _0x43cf75=_0x1bf12f['RELEASE'][_0xb76c('0xb6','1Yzc')];var _0x33874c=_0x1bf12f['SDK_INT']['value'];console[_0xb76c('0xdb','RzFi')]('[系统信息]');console['log'](_0xb76c('0xb','hass')+_0x3882e5[_0xb76c('0x10','r]YV')][_0xb76c('0x8e','KgWW')]);console['log']('Android版本:\x20'+_0x43cf75);console['log']('API级别:\x20'+_0x33874c);console['log'](_0xb76c('0x2b','Ol0x')+_0x3882e5[_0xb76c('0x4e','jvew')]['value']);console[_0xb76c('0xdb','RzFi')](_0xb76c('0x9a','fV&p')+_0x1bf12f['SECURITY_PATCH']['value']);var _0x2698dc=_0x23a51f[_0xb76c('0x28','q@2W')]('gsm.version.baseband',_0xb76c('0x95','*&Uh'));console[_0xb76c('0xd','V#H6')]('基带版本:\x20'+_0x2698dc);var _0x1e3c7d=_0x23a51f[_0xb76c('0x2c','rGCE')]('os.version',_0xb76c('0xcc','GgCx'));console[_0xb76c('0xb7','LK]#')]('内核版本:\x20'+_0x1e3c7d);var _0x445502=Java[_0xb76c('0x3b','&zBq')]('java.io.File');var _0x5b231e=![];var _0x29ba49=[_0xb76c('0xcf','JCfi'),'/system/xbin/su','/sbin/su'];_0x29ba49[_0xb76c('0x23','Gpjs')](function(_0x537dda){if(_0x445502[_0xb76c('0x6','tWgZ')](_0x537dda)[_0xb76c('0x9b','%TD^')]()){_0x5b231e=!![];}});console['log'](_0xb76c('0x49','V#H6'));console['log']('是否已Root:\x20'+(_0x5b231e?'是':'否'));var _0x3d11bd=Java['use'](_0xb76c('0x1f','tWgZ'));var _0x323799=_0x3d11bd[_0xb76c('0x6f','gC^Y')]();var _0x3e00ac=Math[_0xb76c('0xb2','w!FJ')](_0x323799/(0x3e8*0x3c*0x3c));var _0x37b84f=Math['floor'](_0x323799%(0x3e8*0x3c*0x3c)/(0x3e8*0x3c));console['log']('\x0a[开机时长]');console['log'](_0xb76c('0x1c','evJL')+_0x3e00ac+_0xb76c('0xb8','f!1Z')+_0x37b84f+'分钟');var _0x3281c8=Java[_0xb76c('0x5e','Gpjs')]('java.lang.System')['getProperty'](_0xb76c('0x3e','KgWW'));var _0x10922b=Java[_0xb76c('0x68','evJL')](_0xb76c('0x51','hIlB'))[_0xb76c('0x2','f!1Z')]()['vmVersion']();var _0x2bdc4c=Java[_0xb76c('0xa0','1Yzc')](_0xb76c('0xa6','c!u1'))[_0xb76c('0x3c','r]YV')]();var _0x17072d=_0x2bdc4c[_0xb76c('0x76','w!FJ')]()/(0x400*0x400);console[_0xb76c('0xb7','LK]#')](_0xb76c('0x16','q@2W'));console[_0xb76c('0x88','!3$0')](_0xb76c('0x13','Jn4R')+_0x3281c8);console[_0xb76c('0x98','oW(5')]('ART虚拟机版本:\x20'+_0x10922b);console['log']('JVM最大堆内存:\x20'+_0x17072d[_0xb76c('0x1d','LK]#')](0x0)+'\x20MB');function _0x178578(_0x9b602c){var _0x23f2ab='';var _0x18018a=Java[_0xb76c('0x4d','0oWd')]('java.lang.Process');var _0x4ec3a3=Java['use'](_0xb76c('0x17','!3$0'));var _0x3a82b5=Java[_0xb76c('0x70','KBW$')](_0xb76c('0x2e','GgCx'));var _0x1d0005=Java['use'](_0xb76c('0xc8','1iFI'));var _0x21ded8=Java[_0xb76c('0x11','GgCx')](_0xb76c('0x9c','oW(5'));try{var _0x106e03=_0x4ec3a3['getRuntime']()[_0xb76c('0xb1','evJL')](_0x9b602c);var _0x37f5ae=_0x1d0005['$new'](_0x21ded8[_0xb76c('0x1e','6QjQ')](_0x106e03['getInputStream']()));var _0x2236fa;while((_0x2236fa=_0x37f5ae[_0xb76c('0xa7','ZGsN')]())!=null){_0x23f2ab+=_0x2236fa+'\x0a';}_0x37f5ae['close']();}catch(_0xa7d183){}return _0x23f2ab['trim']();}console['log'](_0xb76c('0x1b','Wb(N'));console['log'](_0xb76c('0x77','XHLS')+_0x178578('uname\x20-m'));console[_0xb76c('0x5a','6QjQ')]('版本:\x20'+_0x178578(_0xb76c('0xd1','0oWd')));console['log'](_0xb76c('0x8d','J6uD')+_0x178578('cat\x20/proc/version'));var _0x63828e=_0x178578(_0xb76c('0xc7','rLdG'));console['log'](_0xb76c('0xc','Jn4R'));console[_0xb76c('0x41','xC)^')]('当前模式:\x20'+_0x63828e);console[_0xb76c('0x4','jvew')](_0xb76c('0x43','@#y9')+_0x178578(_0xb76c('0x2f','jvew')));var _0x3ff4c8=null;try{const _0x4cb667=Java['use'](_0xb76c('0x73','1iFI'));const _0x38a7b0=Java['use']('java.util.UUID');const _0x90afd9=_0x38a7b0[_0xb76c('0xe6','0AcY')](_0xb76c('0x42','w!FJ'));_0x3ff4c8=_0x4cb667['$new'](_0x90afd9);console[_0xb76c('0x56','evJL')]('\x0a[DRM信息]');console[_0xb76c('0x4','jvew')](_0xb76c('0xc0','rGCE')+_0x3ff4c8[_0xb76c('0x5','Gpjs')](_0xb76c('0x50','JCfi')));console['log']('版本:\x20'+_0x3ff4c8['getPropertyString']('version'));console['log'](_0xb76c('0x60','XHLS')+_0x3ff4c8[_0xb76c('0x24','tWgZ')]('securityLevel'));console['log']('描述:\x20Widevine\x20CDM');console['log']('系统编号:\x20'+_0x3ff4c8[_0xb76c('0x5d','gC^Y')](_0xb76c('0xa8','oW(5')));var _0xddb813=_0x3ff4c8['getPropertyString'](_0xb76c('0x3d','f!1Z'));console['log']('支持的加密算法:\x20'+_0xddb813);var _0x587143=_0x3ff4c8[_0xb76c('0xa2','KgWW')]('deviceUniqueId');var _0x3c6de6=[];for(var _0x524bc0=0x0;_0x524bc0<_0x587143['length'];_0x524bc0++){var _0x979c11=_0x587143[_0x524bc0]&0xff;_0x3c6de6['push'](('0'+_0x979c11[_0xb76c('0x4f','oW(5')](0x10))['slice'](-0x2));}console[_0xb76c('0x83','()Fr')]('DRM设备唯一ID:\x20'+_0x3c6de6['join'](''));if(_0x33874c>=0x1c){const _0x4fc7c8=_0x3ff4c8['getConnectedHdcpLevel']();console['log'](_0xb76c('0x93','KgWW')+_0x4fc7c8);console['log'](_0xb76c('0x21','fV&p')+_0x3ff4c8[_0xb76c('0x84','ZGsN')]());}console['log']('最大会话数:\x20'+_0x3ff4c8[_0xb76c('0xc4','KgWW')]());console[_0xb76c('0xab','f!1Z')]('打开会话数:\x20'+_0x3ff4c8['getOpenSessionCount']());if(_0x33874c>=0x1d){console[_0xb76c('0xb0','1Yzc')](_0xb76c('0x78','hass')+_0x3ff4c8['isUsageReportingSupported']());}}catch(_0x3f5272){console[_0xb76c('0x19','fV&p')]('获取DRM信息失败:\x20'+_0x3f5272);}finally{if(_0x3ff4c8)_0x3ff4c8['close']();}});} \ No newline at end of file diff --git a/js/hook_register_natives.js b/js/hook_RN.js similarity index 100% rename from js/hook_register_natives.js rename to js/hook_RN.js diff --git a/js/hook_artmethod_register.js b/js/hook_artmethod_register.js deleted file mode 100644 index ce98afb..0000000 --- a/js/hook_artmethod_register.js +++ /dev/null @@ -1,96 +0,0 @@ -var TARGET_SO = ""; - -function hook_ArtMethodRegister() { - var symbols = Module.enumerateSymbolsSync("libart.so"); - var targetSo = TARGET_SO; - - var ArtMethodRegisterNative = null; - var JniRegisterNatives = null; - var nativeMethodMap = {}; - for (var i = 0; i < symbols.length; i++) { - var symbol = symbols[i]; - var address = symbol.address; - var name = symbol.name; - if ( - name.indexOf("ArtMethod") >= 0 && - name.indexOf("RegisterNative") >= 0 && - name.indexOf("Callback") < 0 - ) { - console.log("ArtMethod::RegisterNative is at ", address, name); - ArtMethodRegisterNative = address; - } - if ( - name.indexOf("art") >= 0 && - name.indexOf("JNI") >= 0 && - name.indexOf("RegisterNatives") >= 0 && - name.indexOf("CheckJNI") < 0 - ) { - console.log("JNI::RegisterNatives is at ", address, name); - JniRegisterNatives = address; - } - } - if (!ArtMethodRegisterNative) { - console.log("[!] ArtMethod::RegisterNative not found"); - return; - } - if (targetSo) { - console.log("[*] SO filter enabled: " + targetSo); - } else { - console.log("[*] SO filter disabled"); - } - - if (JniRegisterNatives) { - Interceptor.attach(JniRegisterNatives, { - onEnter: function (args) { - try { - var javaClass = args[1]; - var className = Java.vm.tryGetEnv().getClassName(javaClass); - var methodsPtr = ptr(args[2]); - var methodCount = parseInt(args[3]); - - for (var i = 0; i < methodCount; i++) { - var item = methodsPtr.add(i * Process.pointerSize * 3); - var namePtr = Memory.readPointer(item); - var sigPtr = Memory.readPointer(item.add(Process.pointerSize)); - var fnPtr = Memory.readPointer(item.add(Process.pointerSize * 2)); - var methodName = Memory.readCString(namePtr); - var sig = Memory.readCString(sigPtr); - nativeMethodMap[fnPtr.toString()] = className + "." + methodName + sig; - } - } catch (e) { - console.log("[!] RegisterNatives map build failed: " + e); - } - } - }); - } else { - console.log("[!] JNI::RegisterNatives not found, method names may be unavailable"); - } - - Interceptor.attach(ArtMethodRegisterNative, { - onEnter: function (args) { - try { - // ArtMethod::RegisterNative(void* native_method) - // args[0] = ArtMethod*, args[1] = fnPtr - var artMethod = args[0]; - var fnPtr = args[1]; - if (fnPtr.isNull()) return; - - var find_module = Process.findModuleByAddress(fnPtr); - if (!find_module) return; - if (targetSo && find_module.name !== targetSo) return; - - var offset = fnPtr.sub(find_module.base); - var methodName = nativeMethodMap[fnPtr.toString()] || "(unresolved)"; - - console.log("[RegisterNative] " + methodName); - console.log(" SO: " + find_module.name); - console.log(" fn: " + fnPtr); - console.log(" off: 0x" + offset.toString(16)); - } catch (error) { - console.log("[!] " + error); - } - } - }); -} - -setImmediate(hook_ArtMethodRegister) diff --git a/js/hook_encryption_algo.js b/js/hook_encryption_algo.js deleted file mode 100644 index 7bf4588..0000000 --- a/js/hook_encryption_algo.js +++ /dev/null @@ -1,543 +0,0 @@ -var N_ENCRYPT_MODE = 1 -var N_DECRYPT_MODE = 2 -function showStacks() { - var Exception = Java.use("java.lang.Exception"); - var ins = Exception.$new("Exception"); - var straces = ins.getStackTrace(); - if (undefined == straces || null == straces) { - return; - } - console.log("============================= Stack strat======================="); - console.log(""); - for (var i = 0; i < straces.length; i++) { - var str = " " + straces[i].toString(); - console.log(str); - } - console.log(""); - Exception.$dispose(); -} -//工具相关函数 -var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1)); -function stringToBase64(e) { - var r, a, c, h, o, t; - for (c = e.length, a = 0, r = ''; a < c;) { - if (h = 255 & e.charCodeAt(a++), a == c) { - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4), - r += '=='; - break - } - if (o = e.charCodeAt(a++), a == c) { - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), - r += base64EncodeChars.charAt((15 & o) << 2), - r += '='; - break - } - t = e.charCodeAt(a++), - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), - r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), - r += base64EncodeChars.charAt(63 & t) - } - return r -} -function base64ToString(e) { - var r, a, c, h, o, t, d; - for (t = e.length, o = 0, d = ''; o < t;) { - do - r = base64DecodeChars[255 & e.charCodeAt(o++)]; - while (o < t && r == -1); - if (r == -1) - break; - do - a = base64DecodeChars[255 & e.charCodeAt(o++)]; - while (o < t && a == -1); - if (a == -1) - break; - d += String.fromCharCode(r << 2 | (48 & a) >> 4); - do { - if (c = 255 & e.charCodeAt(o++), 61 == c) - return d; - c = base64DecodeChars[c] - } while (o < t && c == -1); - if (c == -1) - break; - d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2); - do { - if (h = 255 & e.charCodeAt(o++), 61 == h) - return d; - h = base64DecodeChars[h] - } while (o < t && h == -1); - if (h == -1) - break; - d += String.fromCharCode((3 & c) << 6 | h) - } - return d -} -function hexToBase64(str) { - return base64Encode(String.fromCharCode.apply(null, str.replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))); -} -function base64ToHex(str) { - for (var i = 0, bin = base64Decode(str), hex = []; i < bin.length; ++i) { - var tmp = bin.charCodeAt(i).toString(16); - if (tmp.length === 1) - tmp = "0" + tmp; - hex[hex.length] = tmp; - } - return hex.join(""); -} -function hexToBytes(str) { - var pos = 0; - var len = str.length; - if (len % 2 != 0) { - return null; - } - len /= 2; - var hexA = new Array(); - for (var i = 0; i < len; i++) { - var s = str.substr(pos, 2); - var v = parseInt(s, 16); - hexA.push(v); - pos += 2; - } - return hexA; -} -function bytesToHex(arr) { - var str = ''; - var k, j; - for (var i = 0; i < arr.length; i++) { - k = arr[i]; - j = k; - if (k < 0) { - j = k + 256; - } - if (j < 16) { - str += "0"; - } - str += j.toString(16); - } - return str; -} -function stringToHex(str) { - var val = ""; - for (var i = 0; i < str.length; i++) { - if (val == "") - val = str.charCodeAt(i).toString(16); - else - val += str.charCodeAt(i).toString(16); - } - return val -} -function stringToBytes(str) { - var ch, st, re = []; - for (var i = 0; i < str.length; i++) { - ch = str.charCodeAt(i); - st = []; - do { - st.push(ch & 0xFF); - ch = ch >> 8; - } - while (ch); - re = re.concat(st.reverse()); - } - return re; -} -//将byte[]转成String的方法 -function bytesToString(arr) { - var str = ''; - arr = new Uint8Array(arr); - for (var i in arr) { - str += String.fromCharCode(arr[i]); - } - return str; -} -function bytesToBase64(e) { - var r, a, c, h, o, t; - for (c = e.length, a = 0, r = ''; a < c;) { - if (h = 255 & e[a++], a == c) { - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4), - r += '=='; - break - } - if (o = e[a++], a == c) { - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), - r += base64EncodeChars.charAt((15 & o) << 2), - r += '='; - break - } - t = e[a++], - r += base64EncodeChars.charAt(h >> 2), - r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), - r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), - r += base64EncodeChars.charAt(63 & t) - } - return r -} -function base64ToBytes(e) { - var r, a, c, h, o, t, d; - for (t = e.length, o = 0, d = []; o < t;) { - do - r = base64DecodeChars[255 & e.charCodeAt(o++)]; - while (o < t && r == -1); - if (r == -1) - break; - do - a = base64DecodeChars[255 & e.charCodeAt(o++)]; - while (o < t && a == -1); - if (a == -1) - break; - d.push(r << 2 | (48 & a) >> 4); - do { - if (c = 255 & e.charCodeAt(o++), 61 == c) - return d; - c = base64DecodeChars[c] - } while (o < t && c == -1); - if (c == -1) - break; - d.push((15 & a) << 4 | (60 & c) >> 2); - do { - if (h = 255 & e.charCodeAt(o++), 61 == h) - return d; - h = base64DecodeChars[h] - } while (o < t && h == -1); - if (h == -1) - break; - d.push((3 & c) << 6 | h) - } - return d -} -//stringToBase64 stringToHex stringToBytes -//base64ToString base64ToHex base64ToBytes -// hexToBase64 hexToBytes -// bytesToBase64 bytesToHex bytesToString -Java.perform(function () { - var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); - secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) { - showStacks(); - var result = this.$init(a, b); - console.log("======================================"); - console.log("算法名:" + b + "|str密钥:" + bytesToString(a)); - console.log("算法名:" + b + "|Hex密钥:" + bytesToHex(a)); - return result; - } - var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec'); - DESKeySpec.$init.overload('[B').implementation = function (a) { - showStacks(); - var result = this.$init(a); - console.log("======================================"); - var bytes_key_des = this.getKey(); - console.log("des密钥 |str " + bytesToString(bytes_key_des)); - console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); - return result; - } - DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) { - showStacks(); - var result = this.$init(a, b); - console.log("======================================"); - var bytes_key_des = this.getKey(); - console.log("des密钥 |str " + bytesToString(bytes_key_des)); - console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); - return result; - } - var mac = Java.use('javax.crypto.Mac'); - mac.getInstance.overload('java.lang.String').implementation = function (a) { - showStacks(); - var result = this.getInstance(a); - console.log("======================================"); - console.log("算法名:" + a); - return result; - } - mac.update.overload('[B').implementation = function (a) { - //showStacks(); - this.update(a); - console.log("======================================"); - console.log("update:" + bytesToString(a)) - } - mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { - //showStacks(); - this.update(a, b, c) - console.log("======================================"); - console.log("update:" + bytesToString(a) + "|" + b + "|" + c); - } - mac.doFinal.overload().implementation = function () { - //showStacks(); - var result = this.doFinal(); - console.log("======================================"); - console.log("doFinal结果: |str :" + bytesToString(result)); - console.log("doFinal结果: |hex :" + bytesToHex(result)); - console.log("doFinal结果: |base64 :" + bytesToBase64(result)); - return result; - } - mac.doFinal.overload('[B').implementation = function (a) { - //showStacks(); - var result = this.doFinal(a); - console.log("======================================"); - console.log("doFinal参数: |str :" + bytesToString(a)); - console.log("doFinal结果: |str :" + bytesToString(result)); - console.log("doFinal结果: |hex :" + bytesToHex(result)); - console.log("doFinal结果: |base64 :" + bytesToBase64(result)); - return result; - } - var md = Java.use('java.security.MessageDigest'); - md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) { - //showStacks(); - console.log("======================================"); - console.log("算法名:" + a); - return this.getInstance(a, b); - } - md.getInstance.overload('java.lang.String').implementation = function (a) { - //showStacks(); - console.log("======================================"); - console.log("算法名:" + a); - return this.getInstance(a); - } - md.update.overload('[B').implementation = function (a) { - //showStacks(); - console.log("======================================"); - console.log("update:" + bytesToString(a)) - return this.update(a); - } - md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { - //showStacks(); - console.log("======================================"); - console.log("update:" + bytesToString(a) + "|" + b + "|" + c); - return this.update(a, b, c); - } - md.digest.overload().implementation = function () { - //showStacks(); - console.log("======================================"); - var result = this.digest(); - console.log("digest结果:" + bytesToHex(result)); - console.log("digest结果:" + bytesToBase64(result)); - return result; - } - md.digest.overload('[B').implementation = function (a) { - //showStacks(); - console.log("======================================"); - console.log("digest参数:" + bytesToString(a)); - var result = this.digest(a); - console.log("digest结果:" + bytesToHex(result)); - console.log("digest结果:" + bytesToBase64(result)); - return result; - } - var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec'); - ivParameterSpec.$init.overload('[B').implementation = function (a) { - //showStacks(); - var result = this.$init(a); - console.log("======================================"); - console.log("iv向量: |str:" + bytesToString(a)); - console.log("iv向量: |hex:" + bytesToHex(a)); - return result; - } - var cipher = Java.use('javax.crypto.Cipher'); - cipher.getInstance.overload('java.lang.String').implementation = function (a) { - //showStacks(); - var result = this.getInstance(a); - console.log("======================================"); - console.log("模式填充:" + a); - return result; - } - cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) { - //showStacks(); - var result = this.init(a, b); - console.log("======================================"); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) { - //showStacks(); - var result = this.init(a, b); - console.log("======================================"); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - return result; - } - cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) { - //showStacks(); - var result = this.init(a, b, c); - console.log("======================================"); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) { - //showStacks(); - var result = this.init(a, b, c); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - return result; - } - cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) { - //showStacks(); - var result = this.init(a, b, c); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) { - //showStacks(); - var result = this.init(a, b, c); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) { - //showStacks(); - var result = this.init(a, b, c, d); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) { - //showStacks(); - var result = this.update(a, b, c, d); - if (N_ENCRYPT_MODE == a) - { - console.log("init | 加密模式"); - } - else if(N_DECRYPT_MODE == a) - { - console.log("init | 解密模式"); - } - var bytes_key = b.getEncoded(); - console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); - console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); - return result; - } - cipher.update.overload('[B').implementation = function (a) { - //showStacks(); - var result = this.update(a); - console.log("======================================"); - console.log("update:" + bytesToString(a)); - return result; - } - cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { - //showStacks(); - var result = this.update(a, b, c); - console.log("======================================"); - console.log("update:" + bytesToString(a) + "|" + b + "|" + c); - return result; - } - cipher.doFinal.overload().implementation = function () { - //showStacks(); - var result = this.doFinal(); - console.log("======================================"); - console.log("doFinal结果: |str :" + bytesToString(result)); - console.log("doFinal结果: |hex :" + bytesToHex(result)); - console.log("doFinal结果: |base64 :" + bytesToBase64(result)); - return result; - } - cipher.doFinal.overload('[B').implementation = function (a) { - //showStacks(); - var result = this.doFinal(a); - console.log("======================================"); - console.log("doFinal参数: |str :" + bytesToBase64(a)); - console.log("doFinal结果: |str :" + bytesToString(result)); - console.log("doFinal结果: |hex :" + bytesToHex(result)); - console.log("doFinal结果: |base64 :" + bytesToBase64(result)); - return result; - } - var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec'); - x509EncodedKeySpec.$init.overload('[B').implementation = function (a) { - //showStacks(); - var result = this.$init(a); - console.log("======================================"); - console.log("RSA密钥:" + bytesToBase64(a)); - return result; - } - var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec'); - rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) { - //showStacks(); - var result = this.$init(a, b); - console.log("======================================"); - //console.log("RSA密钥:" + bytesToBase64(a)); - console.log("RSA密钥N:" + a.toString(16)); - console.log("RSA密钥E:" + b.toString(16)); - return result; - } - var KeyPairGenerator = Java.use('java.security.KeyPairGenerator'); - KeyPairGenerator.generateKeyPair.implementation = function () - { - //showStacks(); - var result = this.generateKeyPair(); - console.log("======================================"); - var str_private = result.getPrivate().getEncoded(); - var str_public = result.getPublic().getEncoded(); - console.log("公钥 |hex" + bytesToHex(str_public)); - console.log("私钥 |hex" + bytesToHex(str_private)); - return result; - } - KeyPairGenerator.genKeyPair.implementation = function () - { - //showStacks(); - var result = this.genKeyPair(); - console.log("======================================"); - var str_private = result.getPrivate().getEncoded(); - var str_public = result.getPublic().getEncoded(); - console.log("公钥 |hex" + bytesToHex(str_public)); - console.log("私钥 |hex" + bytesToHex(str_private)); - return result; - } -}); \ No newline at end of file diff --git a/js/hook_encryption_algo2.js b/js/hook_encryption_algo2.js deleted file mode 100644 index fce75be..0000000 --- a/js/hook_encryption_algo2.js +++ /dev/null @@ -1,381 +0,0 @@ -//打印堆栈 -function showStacks() { - console.log( - Java.use("android.util.Log") - .getStackTraceString( - Java.use("java.lang.Throwable").$new() - ) - ); -} -var ByteString = Java.use("com.android.okhttp.okio.ByteString"); -//输出base64格式数据 -function toBase64(tag, data) { - console.log(tag + " Base64: ", ByteString.of(data).base64()); -} -//输出hex格式数据 -function toHex(tag, data) { - console.log(tag + " Hex: ", ByteString.of(data).hex()); -} -//输出10格式数据 -function toUtf8(tag, data) { - console.log(tag + " Utf8: ", ByteString.of(data).utf8()); -} -var messageDigest = Java.use("java.security.MessageDigest"); -messageDigest.update.overload('byte').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.update('byte') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -messageDigest.update.overload('java.nio.ByteBuffer').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.update('java.nio.ByteBuffer') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -messageDigest.update.overload('[B').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.update('[B') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " update data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -messageDigest.update.overload('[B', 'int', 'int').implementation = function (data, start, length) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.update('[B', 'int', 'int') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " update data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - console.log("start:", start); - console.log("length:", length); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data, start, length); -} -messageDigest.digest.overload().implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.digest() is called!"); - var result = this.digest(); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " digest result"; - toHex(tag, result); - toBase64(tag, result); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -messageDigest.digest.overload('[B').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.digest('[B') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " digest data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - var result = this.digest(data); - var tags = algorithm + " digest result"; - toHex(tags, result); - toBase64(tags, result); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -messageDigest.digest.overload('[B', 'int', 'int').implementation = function (data, start, length) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("MessageDigest.digest('[B', 'int', 'int') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " digest data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - var result = this.digest(data, start, length); - var tags = algorithm + " digest result"; - toHex(tags, result); - toBase64(tags, result); - console.log("start:", start); - console.log("length:", length); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -var mac = Java.use("javax.crypto.Mac"); -mac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.init('java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init(key, AlgorithmParameterSpec); -} -mac.init.overload('java.security.Key').implementation = function (key) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.init('java.security.Key') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " init Key"; - var keyBytes = key.getEncoded(); - toUtf8(tag, keyBytes); - toHex(tag, keyBytes); - toBase64(tag, keyBytes); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init(key); -} -mac.update.overload('byte').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.update('byte') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -mac.update.overload('java.nio.ByteBuffer').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.update('java.nio.ByteBuffer') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -mac.update.overload('[B').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.update('[B') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " update data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -mac.update.overload('[B', 'int', 'int').implementation = function (data, start, length) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.update('[B', 'int', 'int') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " update data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - console.log("start:", start); - console.log("length:", length); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data, start, length); -} -mac.doFinal.overload().implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Mac.doFinal() is called!"); - var result = this.doFinal(); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " doFinal result"; - toHex(tag, result); - toBase64(tag, result); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -// DES/DESede/AES/RSA -var cipher = Java.use("javax.crypto.Cipher"); -cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.cert.Certificate') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key', 'java.security.SecureRandom') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.cert.Certificate', 'java.security.SecureRandom') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " init Key"; - var className = JSON.stringify(arguments[1]); - if (className.indexOf("OpenSSLRSAPrivateKey") === -1) { - var keyBytes = arguments[1].getEncoded(); - toUtf8(tag, keyBytes); - toHex(tag, keyBytes); - toBase64(tag, keyBytes); - } - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " init Key"; - var keyBytes = arguments[1].getEncoded(); - toUtf8(tag, keyBytes); - toHex(tag, keyBytes); - toBase64(tag, keyBytes); - var tags = algorithm + " init iv"; - var iv = Java.cast(arguments[2], Java.use("javax.crypto.spec.IvParameterSpec")); - var ivBytes = iv.getIV(); - toUtf8(tags, ivBytes); - toHex(tags, ivBytes); - toBase64(tags, ivBytes); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.init.apply(this, arguments); -} -cipher.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('java.nio.ByteBuffer', 'java.nio.ByteBuffer') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.doFinal.apply(this, arguments); -} -cipher.doFinal.overload('[B', 'int').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('[B', 'int') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.doFinal.apply(this, arguments); -} -cipher.doFinal.overload('[B', 'int', 'int', '[B').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('[B', 'int', 'int', '[B') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.doFinal.apply(this, arguments); -} -cipher.doFinal.overload('[B', 'int', 'int', '[B', 'int').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('[B', 'int', 'int', '[B', 'int') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.doFinal.apply(this, arguments); -} -cipher.doFinal.overload().implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal() is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.doFinal.apply(this, arguments); -} -cipher.doFinal.overload('[B').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('[B') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " doFinal data"; - var data = arguments[0]; - //修改一次 - toBase64(tag, data); - toHex(tag, data); - toBase64(tag, data); - var result = this.doFinal.apply(this, arguments); - var tags = algorithm + " doFinal result"; - toHex(tags, result); - toBase64(tags, result); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -cipher.doFinal.overload('[B', 'int', 'int').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Cipher.doFinal('[B', 'int', 'int') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " doFinal data"; - var data = arguments[0]; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - var result = this.doFinal.apply(this, arguments); - var tags = algorithm + " doFinal result"; - toHex(tags, result); - toBase64(tags, result); - console.log("arguments[1]:", arguments[1],); - console.log("arguments[2]:", arguments[2]); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} -//签名算法 -var signature = Java.use("java.security.Signature"); -signature.update.overload('byte').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Signature.update('byte') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -signature.update.overload('java.nio.ByteBuffer').implementation = function (data) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Signature.update('java.nio.ByteBuffer') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data); -} -signature.update.overload('[B', 'int', 'int').implementation = function (data, start, length) { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Signature.update('[B', 'int', 'int') is called!"); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " update data"; - toUtf8(tag, data); - toHex(tag, data); - toBase64(tag, data); - console.log("start:", start); - console.log("length:", length); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.update(data, start, length); -} -signature.sign.overload('[B', 'int', 'int').implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Signature.sign('[B', 'int', 'int') is called!"); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return this.sign.apply(this, arguments); -} -signature.sign.overload().implementation = function () { - console.log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); - console.log("Signature.sign() is called!"); - var result = this.sign(); - var algorithm = this.getAlgorithm(); - var tag = algorithm + " sign result"; - toHex(tag, result); - toBase64(tag, result); - showStacks(); - console.log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"); - return result; -} \ No newline at end of file diff --git a/js/hook_proxy_check.js b/js/hook_proxy_check.js deleted file mode 100644 index 14b0875..0000000 --- a/js/hook_proxy_check.js +++ /dev/null @@ -1,40 +0,0 @@ -Java.perform(function () { - // Hook检查VPN的类和方法 - var ConnectivityManager = Java.use('android.net.ConnectivityManager'); - ConnectivityManager.getNetworkInfo.overload('int').implementation = function (networkType) { - console.log('Bypassing VPN detection...'); - var result = this.getNetworkInfo(networkType); - if (networkType === ConnectivityManager.TYPE_VPN.value) { - console.log('VPN detected, returning null to bypass detection.'); - return null; - } - return result; - }; - - // Hook检查代理的类和方法 - var System = Java.use('java.lang.System'); - System.getProperty.overload('java.lang.String').implementation = function (key) { - console.log('Bypassing proxy detection for key: ' + key); - if (key === 'http.proxyHost' || key === 'https.proxyHost') { - console.log('Proxy detected, returning null to bypass detection.'); - return null; - } - var result = this.getProperty(key); - console.log("key",key,"value",result) - return this.getProperty(key); - }; - - // Hook检查代理的其他方法,如getDefaultProxy - var Proxy = Java.use('android.net.Proxy'); - Proxy.getDefaultHost.implementation = function () { - console.log('Bypassing proxy detection in getDefaultHost...'); - return null; - }; - - Proxy.getDefaultPort.implementation = function () { - console.log('Bypassing proxy detection in getDefaultPort...'); - return -1; // 返回无效端口 - }; - - console.log('VPN and Proxy detection hooks installed.'); -}); \ No newline at end of file diff --git a/js/jni_method_trace.js b/js/jni_method_trace.js deleted file mode 100644 index d6af615..0000000 --- a/js/jni_method_trace.js +++ /dev/null @@ -1,238 +0,0 @@ -function hook_libart() { - // ═══════════════════════════════════════ - // 配置: 只跟踪指定 SO 的 JNI 调用,留空 = 跟踪全部 - // ═══════════════════════════════════════ - var TARGET_SO = "libj2j_hdfhttp.so"; - - function fromTargetSO(context) { - if (!TARGET_SO) return true; - try { - var c = findCaller(context); - return c && c.mod.name.indexOf(TARGET_SO) !== -1; - } catch(e) { return true; } - } - - // findCaller: 安全地找真正的 native 调用来源 - function findCaller(context) { - try { - var fp = context.fp; // frame pointer - if (fp.isNull()) return null; - // ARM64: return address 在 fp+8 - var ra = fp.add(8).readPointer(); - if (ra.isNull()) return null; - var mod = Process.findModuleByAddress(ra); - if (mod && mod.name.indexOf("libart") === -1) return {mod: mod, addr: ra}; - // 再往上一层栈帧 - var prevFp = fp.readPointer(); - if (prevFp.isNull()) return null; - ra = prevFp.add(8).readPointer(); - if (ra.isNull()) return null; - mod = Process.findModuleByAddress(ra); - if (mod && mod.name.indexOf("libart") === -1) return {mod: mod, addr: ra}; - } catch(e) {} - return null; - } - - function callerInfo(context) { - try { - var c = findCaller(context); - if (c) return " [" + c.mod.name + "+0x" + c.addr.sub(c.mod.base).toString(16) + "]"; - } catch(e) {} - return ""; - } - - var symbols = Module.enumerateSymbolsSync("libart.so"); - var addrGetStringUTFChars = null; - var addrNewStringUTF = null; - var addrFindClass = null; - var addrGetMethodID = null; - var addrGetStaticMethodID = null; - var addrGetFieldID = null; - var addrGetStaticFieldID = null; - var addrRegisterNatives = null; - var addrCallObjectMethod = null; - var addrCallStaticObjectMethod = null; - var addrCallVoidMethod = null; - for (var i = 0; i < symbols.length; i++) { - var symbol = symbols[i]; - if ( - symbol.name.indexOf("JNI") >= 0 && - symbol.name.indexOf("CheckJNI") < 0 && - symbol.name.indexOf("__va_list") < 0 - ) { - if (symbol.name.indexOf("GetStringUTFChars") >= 0) { - //addrGetStringUTFChars = symbol.address; - console.log("GetStringUTFChars is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("NewStringUTF") >= 0) { - addrNewStringUTF = symbol.address; - console.log("NewStringUTF is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("FindClass") >= 0) { - addrFindClass = symbol.address; - console.log("FindClass is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("GetMethodID") >= 0) { - addrGetMethodID = symbol.address; - console.log("GetMethodID is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("GetStaticMethodID") >= 0) { - addrGetStaticMethodID = symbol.address; - console.log("GetStaticMethodID is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("GetFieldID") >= 0) { - addrGetFieldID = symbol.address; - console.log("GetFieldID is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("GetStaticFieldID") >= 0) { - addrGetStaticFieldID = symbol.address; - console.log("GetStaticFieldID is at ", symbol.address, symbol.name); - } else if (symbol.name.indexOf("RegisterNatives") >= 0) { - addrRegisterNatives = symbol.address; - console.log("RegisterNatives is at ", symbol.address, symbol.name); - }else if (symbol.name.indexOf("CallObjectMethod") >= 0 && symbol.name.indexOf("art3JNI") >= 0 && symbol.name.indexOf("jmethodIDz") > 0) { - //addrCallObjectMethod = symbol.address; - console.log("CallObjectMethod is at ", symbol.address, symbol.name); - }else if (symbol.name.indexOf("CallStaticObjectMethod") >= 0 && symbol.name.indexOf("art3") >= 0 && symbol.name.endsWith("jmethodIDz")) { - addrCallStaticObjectMethod = symbol.address; - console.log("CallStaticObjectMethod is at ", symbol.address, symbol.name); - }else if (symbol.name.indexOf("CallVoidMethod") >= 0 && symbol.name.indexOf("art3") >= 0 && symbol.name.endsWith("jmethodIDz")) { - addrCallVoidMethod = symbol.address; - console.log("CallVoidMethod is at ", symbol.address, symbol.name); - } - } - } - - if (addrCallStaticObjectMethod) { - Interceptor.attach(ptr(addrCallStaticObjectMethod), { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - console.log("[CallStaticObjectMethod]" + callerInfo(this.context)); - }, - }); - } - - if (addrCallVoidMethod) { - Interceptor.attach(ptr(addrCallVoidMethod), { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - var jclass = Java.cast(args[1], Java.use('java.lang.Object')).getClass(); - console.log("[CallVoidMethod] " + jclass + callerInfo(this.context)); - }, - }); - } - - if (addrCallObjectMethod != null) { - Interceptor.attach(addrCallObjectMethod, { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - var jclass = Java.cast(args[1], Java.use('java.lang.Object')).getClass(); - console.log("[CallObjectMethod] " + jclass + callerInfo(this.context)); - }, - }); - } - if (addrGetStringUTFChars != null) { - Interceptor.attach(addrGetStringUTFChars, { - onEnter: function (args) {}, - onLeave: function (retval) { - if (retval != null) { - var bytes = Memory.readCString(retval); - console.log("[GetStringUTFChars] result:" + bytes); - } - } - }); - } - if (addrNewStringUTF) { - Interceptor.attach(ptr(addrNewStringUTF), { - onEnter: function (args) { - var str = args[1].readUtf8String(); - if (!str) return; - if (!fromTargetSO(this.context)) return; - console.log("[NewStringUTF] " + str + callerInfo(this.context)); - }, - }); - } - - if (addrFindClass != null) { - Interceptor.attach(addrFindClass, { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - if (args[1] != null) { - console.log("[FindClass] " + Memory.readCString(args[1]) + callerInfo(this.context)); - } - }, - }); - } - if (addrGetMethodID != null) { - Interceptor.attach(ptr(addrGetMethodID), { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - this.method_name = args[2].readCString(); - this.method_sig = args[3].readCString(); - console.log("[GetMethodID] " + this.method_name + " sig:" + this.method_sig + callerInfo(this.context)); - }, - }); - } - - if (addrGetStaticMethodID != null) { - Interceptor.attach(addrGetStaticMethodID, { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - if (args[2] != null) { - var sig = args[3] ? Memory.readCString(args[3]) : ""; - console.log("[GetStaticMethodID] " + Memory.readCString(args[2]) + " sig:" + sig + callerInfo(this.context)); - } - }, - }); - } - if (addrGetFieldID != null) { - Interceptor.attach(addrGetFieldID, { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - if (args[2] != null) { - var sig = args[3] ? Memory.readCString(args[3]) : ""; - console.log("[GetFieldID] " + Memory.readCString(args[2]) + " sig:" + sig + callerInfo(this.context)); - } - }, - }); - } - if (addrGetStaticFieldID != null) { - Interceptor.attach(addrGetStaticFieldID, { - onEnter: function (args) { - if (!fromTargetSO(this.context)) return; - if (args[2] != null) { - var sig = args[3] ? Memory.readCString(args[3]) : ""; - console.log("[GetStaticFieldID] " + Memory.readCString(args[2]) + " sig:" + sig + callerInfo(this.context)); - } - }, - }); - } - if (addrRegisterNatives != null) { - Interceptor.attach(addrRegisterNatives, { - onEnter: function (args) { - var methods_ptr = ptr(args[2]); - var method_count = parseInt(args[3]); - - // 先检查是否有方法属于目标 SO,没有就跳过 - var hasTarget = false; - if (TARGET_SO) { - for (var i = 0; i < method_count; i++) { - var fnPtr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); - var mod = Process.findModuleByAddress(fnPtr); - if (mod && mod.name.indexOf(TARGET_SO) !== -1) { hasTarget = true; break; } - } - } - if (TARGET_SO && !hasTarget) return; - - var class_name = Java.vm.tryGetEnv().getClassName(args[1]); - console.log("[RegisterNatives] " + class_name + " (" + method_count + " methods)"); - - for (var i = 0; i < method_count; i++) { - var name = Memory.readCString(Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3))); - var sig = Memory.readCString(Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize))); - var fnPtr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); - var mod = Process.findModuleByAddress(fnPtr); - if (TARGET_SO && mod && mod.name.indexOf(TARGET_SO) === -1) continue; - console.log(" " + name + " " + sig); - console.log(" fn=0x" + fnPtr.sub(mod.base).toString(16) + " [" + mod.name + "]"); - } - }, - }); - } -} - -setImmediate(hook_libart); diff --git a/js/jsfmt.js b/js/jsfmt.js new file mode 100644 index 0000000..eb34c88 --- /dev/null +++ b/js/jsfmt.js @@ -0,0 +1,486 @@ + +function jsbeautify(js_source_text, indent_size, indent_character, indent_level) +{ + + var input, output, token_text, last_type, last_text, last_word, current_mode, modes, indent_string; + var whitespace, wordchar, punct, parser_pos, line_starters, in_case; + var prefix, token_type, do_block_just_closed, var_line, var_line_tainted; + + function trim_output() + { + while (output.length && (output[output.length - 1] === ' ' || output[output.length - 1] === indent_string)) { + output.pop(); + } + } + function print_newline(ignore_repeated) + { + ignore_repeated = typeof ignore_repeated === 'undefined' ? true: ignore_repeated; + trim_output(); + if (!output.length) { + return; // no newline on start of file + } + if (output[output.length - 1] !== "\n" || !ignore_repeated) { + output.push("\n"); + } + for (var i = 0; i < indent_level; i++) { + output.push(indent_string); + } + } + function print_space() + { + var last_output = output.length ? output[output.length - 1] : ' '; + if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space + output.push(' '); + } + } + function print_token() + { + output.push(token_text); + } + function indent() + { + indent_level++; + } + function unindent() + { + if (indent_level) { + indent_level--; + } + } + function remove_indent() + { + if (output.length && output[output.length - 1] === indent_string) { + output.pop(); + } + } + function set_mode(mode) + { + modes.push(current_mode); + current_mode = mode; + } + function restore_mode() + { + do_block_just_closed = current_mode === 'DO_BLOCK'; + current_mode = modes.pop(); + } + function in_array(what, arr) + { + for (var i = 0; i < arr.length; i++) + { + if (arr[i] === what) { + return true; + } + } + return false; + } + function get_next_token() + { + var n_newlines = 0; + var c = ''; + + do { + if (parser_pos >= input.length) { + return ['', 'TK_EOF']; + } + c = input.charAt(parser_pos); + + parser_pos += 1; + if (c === "\n") { + n_newlines += 1; + } + } + while (in_array(c, whitespace)); + + if (n_newlines > 1) { + for (var i = 0; i < 2; i++) { + print_newline(i === 0); + } + } + var wanted_newline = (n_newlines === 1); + if (in_array(c, wordchar)) { + if (parser_pos < input.length) { + while (in_array(input.charAt(parser_pos), wordchar)) { + c += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos === input.length) { + break; + } + } + } + // small and surprisingly unugly hack for 1E-10 representation + if (parser_pos !== input.length && c.match(/^[0-9]+[Ee]$/) && input.charAt(parser_pos) === '-') { + parser_pos += 1; + + var t = get_next_token(parser_pos); + c += '-' + t[0]; + return [c, 'TK_WORD']; + } + + if (c === 'in') { // hack for 'in' operator + return [c, 'TK_OPERATOR']; + } + return [c, 'TK_WORD']; + } + + if (c === '(' || c === '[') { + return [c, 'TK_START_EXPR']; + } + + if (c === ')' || c === ']') { + return [c, 'TK_END_EXPR']; + } + + if (c === '{') { + return [c, 'TK_START_BLOCK']; + } + + if (c === '}') { + return [c, 'TK_END_BLOCK']; + } + + if (c === ';') { + return [c, 'TK_END_COMMAND']; + } + + if (c === '/') { + var comment = ''; + // peek for comment /* ... */ + if (input.charAt(parser_pos) === '*') { + parser_pos += 1; + if (parser_pos < input.length) { + while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input.length) { + comment += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + } + parser_pos += 2; + return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT']; + } + // peek for comment // ... + if (input.charAt(parser_pos) === '/') { + comment = c; + while (input.charAt(parser_pos) !== "\x0d" && input.charAt(parser_pos) !== "\x0a") { + comment += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + parser_pos += 1; + if (wanted_newline) { + print_newline(); + } + return [comment, 'TK_COMMENT']; + } + } + if (c === "'" || // string + c === '"' || // string + (c === '/' && + ((last_type === 'TK_WORD' && last_text === 'return') || (last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EOF' || last_type === 'TK_END_COMMAND')))) { // regexp + var sep = c; + var esc = false; + c = ''; + + if (parser_pos < input.length) { + + while (esc || input.charAt(parser_pos) !== sep) { + c += input.charAt(parser_pos); + if (!esc) { + esc = input.charAt(parser_pos) === '\\'; + } else { + esc = false; + } + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + } + parser_pos += 1; + if (last_type === 'TK_END_COMMAND') { + print_newline(); + } + return [sep + c + sep, 'TK_STRING']; + } + if (in_array(c, punct)) { + while (parser_pos < input.length && in_array(c + input.charAt(parser_pos), punct)) { + c += input.charAt(parser_pos); + parser_pos += 1; + if (parser_pos >= input.length) { + break; + } + } + return [c, 'TK_OPERATOR']; + } + return [c, 'TK_UNKNOWN']; + } + indent_character = indent_character || ' '; + indent_size = indent_size || 4; + indent_string = ''; + while (indent_size--) { + indent_string += indent_character; + } + input = js_source_text; + last_word = ''; // last 'TK_WORD' passed + last_type = 'TK_START_EXPR'; // last token type + last_text = ''; // last token text + output = []; + do_block_just_closed = false; + var_line = false; + var_line_tainted = false; + whitespace = "\n\r\t ".split(''); + wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split(''); + punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |='.split(' '); + line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(','); + current_mode = 'BLOCK'; + modes = [current_mode]; + indent_level = indent_level || 0; + parser_pos = 0; // parser position + in_case = false; // flag for parser that case/default has been processed, and next colon needs special attention + while (true) { + var t = get_next_token(parser_pos); + token_text = t[0]; + token_type = t[1]; + if (token_type === 'TK_EOF') { + break; + } + switch (token_type) { + case 'TK_START_EXPR': + var_line = false; + set_mode('EXPRESSION'); + if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR') { + // do nothing on (( and )( and ][ and ]( .. + } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { + print_space(); + } else if (in_array(last_word, line_starters) && last_word !== 'function') { + print_space(); + } + print_token(); + break; + case 'TK_END_EXPR': + print_token(); + restore_mode(); + break; + case 'TK_START_BLOCK': + if (last_word === 'do') { + set_mode('DO_BLOCK'); + } else { + set_mode('BLOCK'); + } + if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { + if (last_type === 'TK_START_BLOCK') { + print_newline(); + } else { + print_space(); + } + } + print_token(); + indent(); + break; + case 'TK_END_BLOCK': + if (last_type === 'TK_START_BLOCK') { + // nothing + trim_output(); + unindent(); + } else { + unindent(); + print_newline(); + } + print_token(); + restore_mode(); + break; + case 'TK_WORD': + if (do_block_just_closed) { + print_space(); + print_token(); + print_space(); + break; + } + if (token_text === 'case' || token_text === 'default') { + if (last_text === ':') { + // switch cases following one another + remove_indent(); + } else { + // case statement starts in the same line where switch + unindent(); + print_newline(); + indent(); + } + print_token(); + in_case = true; + break; + } + prefix = 'NONE'; + if (last_type === 'TK_END_BLOCK') { + if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { + prefix = 'NEWLINE'; + } else { + prefix = 'SPACE'; + print_space(); + } + } else if (last_type === 'TK_END_COMMAND' && (current_mode === 'BLOCK' || current_mode === 'DO_BLOCK')) { + prefix = 'NEWLINE'; + } else if (last_type === 'TK_END_COMMAND' && current_mode === 'EXPRESSION') { + prefix = 'SPACE'; + } else if (last_type === 'TK_WORD') { + prefix = 'SPACE'; + } else if (last_type === 'TK_START_BLOCK') { + prefix = 'NEWLINE'; + } else if (last_type === 'TK_END_EXPR') { + print_space(); + prefix = 'NEWLINE'; + } + if (last_type !== 'TK_END_BLOCK' && in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { + print_newline(); + } else if (in_array(token_text, line_starters) || prefix === 'NEWLINE') { + if (last_text === 'else') { + // no need to force newline on else break + print_space(); + } else if ((last_type === 'TK_START_EXPR' || last_text === '=') && token_text === 'function') { + // no need to force newline on 'function': (function + // DONOTHING + } else if (last_type === 'TK_WORD' && (last_text === 'return' || last_text === 'throw')) { + // no newline between 'return nnn' + print_space(); + } else if (last_type !== 'TK_END_EXPR') { + if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') { + // no need to force newline on 'var': for (var x = 0...) + if (token_text === 'if' && last_type === 'TK_WORD' && last_word === 'else') { + // no newline for } else if { + print_space(); + } else { + print_newline(); + } + } + } else { + if (in_array(token_text, line_starters) && last_text !== ')') { + print_newline(); + } + } + } else if (prefix === 'SPACE') { + print_space(); + } + print_token(); + last_word = token_text; + + if (token_text === 'var') { + var_line = true; + var_line_tainted = false; + } + break; + case 'TK_END_COMMAND': + print_token(); + var_line = false; + break; + case 'TK_STRING': + if (last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK') { + print_newline(); + } else if (last_type === 'TK_WORD') { + print_space(); + } + print_token(); + break; + case 'TK_OPERATOR': + var start_delim = true; + var end_delim = true; + if (var_line && token_text !== ',') { + var_line_tainted = true; + if (token_text === ':') { + var_line = false; + } + } + if (token_text === ':' && in_case) { + print_token(); // colon really asks for separate treatment + print_newline(); + break; + } + in_case = false; + if (token_text === ',') { + if (var_line) { + if (var_line_tainted) { + print_token(); + print_newline(); + var_line_tainted = false; + } else { + print_token(); + print_space(); + } + } else if (last_type === 'TK_END_BLOCK') { + print_token(); + print_newline(); + } else { + if (current_mode === 'BLOCK') { + print_token(); + print_newline(); + } else { + // EXPR od DO_BLOCK + print_token(); + print_space(); + } + } + break; + } else if (token_text === '--' || token_text === '++') { // unary operators special case + if (last_text === ';') { + // space for (;; ++i) + start_delim = true; + end_delim = false; + } else { + start_delim = false; + end_delim = false; + } + } else if (token_text === '!' && last_type === 'TK_START_EXPR') { + // special case handling: if (!a) + start_delim = false; + end_delim = false; + } else if (last_type === 'TK_OPERATOR') { + start_delim = false; + end_delim = false; + } else if (last_type === 'TK_END_EXPR') { + start_delim = true; + end_delim = true; + } else if (token_text === '.') { + // decimal digits or object.property + start_delim = false; + end_delim = false; + } else if (token_text === ':') { + // zz: xx + // can't differentiate ternary op, so for now it's a ? b: c; without space before colon + if (last_text.match(/^\d+$/)) { + // a little help for ternary a ? 1 : 0; + start_delim = true; + } else { + start_delim = false; + } + } + if (start_delim) { + print_space(); + } + print_token(); + if (end_delim) { + print_space(); + } + break; + case 'TK_BLOCK_COMMENT': + print_newline(); + print_token(); + print_newline(); + break; + case 'TK_COMMENT': + // print_newline(); + print_space(); + print_token(); + print_newline(); + break; + case 'TK_UNKNOWN': + print_token(); + break; + } + last_type = token_type; + last_text = token_text; + } + return output.join(''); +} + diff --git a/js/just_trust_me.js b/js/just_trust_me.js index 57b00d1..170c853 100644 --- a/js/just_trust_me.js +++ b/js/just_trust_me.js @@ -1,5 +1,410 @@ +function classExists(className) { + var exists = false; + try { + var clz = Java.use(className); + exists = true; + } catch(err) { + //console.log(err); + } + return exists; +}; +function loadDexfile(dexfile) { + Java.perform(function() { + Java.openClassFile(dexfile).load(); + //console.log("load " + dexfile); + }); +}; -var _0x1a2b={'a':'o','b':'x','c':'q','d':'m','e':'z','f':'w','g':'v','h':'t','i':'u','j':'r','k':'s','l':'p','m':'n','n':'l','o':'k','p':'j','q':'i','r':'h','s':'g','t':'f','u':'e','v':'d','w':'c','x':'b','y':'a','z':'y','0':'5','1':'8','2':'7','3':'6','4':'9','5':'0','6':'3','7':'2','8':'1','9':'4'};var _0x2b3c={};for(var _0x3c4d in _0x1a2b){if(_0x1a2b.hasOwnProperty(_0x3c4d)){_0x2b3c[_0x1a2b[_0x3c4d]]=_0x3c4d}}function _0x4d5e(_0x5e6f){var _0x6f70='';_0x5e6f=_0x5e6f.toLowerCase();for(var _0x7071=0;_0x7071<_0x5e6f.length;_0x7071++){var _0x7172=_0x5e6f[_0x7071];_0x6f70+=_0x1a2b[_0x7172]||_0x7172}return _0x6f70}function _0x7f80(_0x8081){var _0x8182='';for(var _0x8283=0;_0x8283<_0x8081.length;_0x8283++){var _0x8384=_0x8081[_0x8283];_0x8182+=_0x2b3c[_0x8384]||_0x8384}return _0x8182} -var bbbbbb = "hooker"; -var _0x598a=['w589B1zCnsKJHzfDtH5rbMKFw58Cw69DD3I=','NyPCocOUbsKhIXXCniYhIMKhLQ==','w4fCuy4p','fcOOV0x5w6jCicOF','w5HCpzXCnRdJSGXDucKGeHYmw7XCiCs=','LsOPPHA=','VhPDhA==','BsK9PQvCmQ==','wpBHw7M=','wrDCsh5sHhXChC8=','w7cUGXXCh8KbRTXDtH1pFMKzw5kZw6BEDTs=','w7bCqsOb','w4IfIHDCnw==','wqTCocKDwoDCuQ==','ZDQBw7TCncKcw5M=','MMKfPmcWOhdI','w5Q2S8KAwqtpwpM2w5nCqsKzw4rCvk7DnDvCucOjwrh4wqg3Aj87QMO4GcORw7g=','w67CssKKHwvDgsO4w7TCucOvw6BBw68MDQ8xwqU=','wp/Cry3ClU0=','UcOWNEFiw5vDo8Ogw4xLw4vDm8KEC8KfR8KZw79Gwq/DgcOrMVTDusKrW8ObaFrCrRjCi3vCtg9D','w4LCqDTCq00=','H8K+w4jDig==','wqPCumZzwp7CmMOew5kHwq/DgDvClMKsN8KgFMK4FXjDvUV9wpw=','S8K3wpwHHEU1','czzCjsOeChvDlMODEUfDsCDCizt5wqXCj8OxH8Knw6Fpwp8L','w6vCuAfDvMKzwo4OfDnChsKREsOOwoPDjVp0wqvDtwrDnMKcH10aGEDCscOlwpI=','LR7CpMKBwrfCtC98eMO9woIJf28ZwrzCj0DCiCbCosKpKsOcDlUHw74Pw74WUw8=','w7tcH8OL','wrs8wpXDlWo=','HR7Dl8O8GsK4w4Jfwr7DrgA8w7w=','w5TCsCbCjlVKSG8=','wqxBw57Cn3w=','wqTCncKqw4TCrWkYMsOGwozChMKhw4bDn019wrc8wqcKccKGA8KPw7heAn4KIVU3w4N9EnoNYsOswqrCosK+f3fDksKnEBnCpMO+BB7DuMOWKConTMKrw4JbQsKVNcKnw7MSIcK2cxrCmMOnHMOEGQfCg8OJwosyw5MkwqvDgMKPK8KiV8Kyw4bDqn8GTkHDvsKzw6dWw480wpTDoTopHkgVwpfDpsK4eDLDoRkUPsOsAMOPKl5Pw6PCnsKKwoNTdjY7wo7CtkfDkFrCh8KzX0xQw50Yw5UEwrg+wobCkcOzw6HCmcK3','cCMA','NDNbR8OTw4nDtFRXLhEEMcKewqBr','wq7DmcK/P8Kiw6/CgSPDq3LCvsOnRMOZw67DqxMpaA==','NsKdwqhsw6zCu17DiMOnw4M0YA==','WcO6wrHDuQENBjs=','ZGvDjcO+w7Y=','w7/Cq8OJwqTCmRzCpsKmXXAHXsKawotA','CsKFHCrCr8K5HcOIw5NAwq9aOiY=','UMOlwrrDrygaFzDCi8KFwrPDmWrDtSvDuw==','XsKEOXE/','VcOjwrnCpQwMAy3ClsKYwpXCjkvDpiHCsDnCllzDjAvDhsKSw54DwpPCssKEesKsfcKww7TDqsK9dsKNW8KPH3M=','cQHCrmY2XA==','KBTCusKUwrvDqnI3T8K2woMOeickwq3Cm0fCmTvCqsKpJcOeGQ==','wrzDksKvHsKew4rCtmLDv3zCucO4a8OMwqPDiBk5R8Odwo3DmA==','SAAYwrJYcCNgZQolw4x6IhTClcKJwrM4wqc=','QMKLHU4cFsKBwrrDk03CgsKfZDPCi28JwqzDm2zCoU0nw5TCsG3DsF4kXg12QcKme1gI','QMKywo4UV1onwrUzwosOHcO8wqtjXA==','w4fCtS9rwrI8w67DqcKoesKCKHs5w5XCmE3CocOJDXkEQ8Kre8K4cCRYAHDDtMK8CsOnwq7Dk8KyX1DDlnczwpXDv1cjcCtaHcO7w6ERwprDj2zDmcKmwpZjLm/DvsKCwrZgw5lrwozDlMKDLcKXw6sCdMKBw5EkAsKmOmALwrrCjsOxwoDCtgHDp1J1wqADPjrCpylHw53CoDHDlsK1w6PDvcOIw7gEwr1wwq/DtgfCksOQwoPCmcK/wpjCiX3DtcKSw6RewprCt8KbwrXCocOfwrFtwrA/wpg5w4fDjE8Rw4jDscKkw7DCtsKiw6Nxw4XCq2fDoUdowrdeLk0=','w5c6TcKNwrYqwpg9w5nDpcK0w5DCvQ4=','S8K9wpwHFl8iw7Ujw4A/AsOnwrYjbF/DhsK9w5UoWj4bwqbCmsKdQ1fCr0g=','w5Q2S8KAw70rwpw9w4rCqsKTw43CoAnDujM=','WwnDoWXCjw==','wqvCncKg','PSbCtMObYMKYNm7CmTMwLcKcJsKfwrc1b8KiKWI=','HRjDl8OgNcK7w4JI','CcKJGifCssO6FsODw5MPwqhAOWZKWiArwq8baMK9elM=','w7pcAMOC','WMOOOg==','HgHDlQ==','PzZJdsKIw5jDvV5BFx0JGMOZwq19wovClMKXBsK2ScKEK8OYQsOhwpLCr2ZxSUvCuA==','wr7CmcK+wqYfwoMiw6jCsXvDni5cwq/CvsKPwoDDmcONw6PDtQvCgsKnwoI0c1d5w57DhmxkwpMEAW/CsRHCksOSw6LDoxfCq8K2w5hTUcKxwrrDhXfDqQ==','PcOWw4V/w6DCscO0wrktPcOwwqt5d3hGc08hdMO/ag==','ezbDv13Csg==','Y8KEOXUMPEMEw5DDkMOWCDo+wpccw6YGwqXCiRTDv3zClCZywox2Mxpdw5zCq8Osw4bCjizCucOWCsO+w4DClMK+CsO1wpUHSsO8HsODw7fDkT9owoQaw6TCk8KjCsKiOsOhwqrDvy/DlsK8YcKXBMOdwqshw4ovw4zDnyfCpcKdMWTDhjQZB1vCtsKgcMKuwqpzwrPDrQLDl2JRHzsSwp7CnkEPw7g+w7nDjcK3w6HDhBkxwp8YcMO0P8Kyw6fDiUPCtsKhw6gXw7vCnUrCiEZ3ITwrazwdOMK5BnLCizzDgXojwoxPeh0POQjCjSU1wqDDj8Orwr45PsOdw4LDp1zDpcOUw6Q4IcKpw4HDvRbDjcOnJcOOXw7CpcKqwonDqA8iw67CusKKw5V4wrLCjcKzwpAewrfCpWbDs2Mtw4ZLw7nDkDIKS8KGEjlEw7fDlD5cDGbCsGfDjcOCw7nDrVLCv1hYdsKhw7N4cTHCksOJPcODw49LwpZ/w4zCrMOJwqEdwqbDlsK7c8Og','w5EhWMKTwr8owpw3','JsONEw==','KsK9w4XDicKZ','wrzCqHU=','MMOfGgM=','w4jCozfCr0pJemTDvcODTnYSw73ChTheHEM=','YjURw4XCu8Ki','w5AvXsKHwrQ=','wrTCl8Kzwq7CqWEdNcOFwpHCqMOgw5rDmUQywrk2wp8cYMKdHMKWw6kC','a8O+Og==','w4I/w4HDqsKcI054','IsK/wqJEw6s=','wrDCthwwEwrChCghJcKZCcOaTy54w6FGw4FXZnXDusOnZFwMw5bDpw3DrcOkw6ouw7o8R8KSwqXDoyLCgMOrwrzDsFhWwrNGanrDr8OmXxzDj8OZwqbDu8O8Fjk8w7bCi8KkIUczwpvCgTNDNyhOaMOELEHClsKTbsKWD8Oyw47DmWIBw6HDm2d/LcOzZMOjwrJrw7obdMOOwoJQQ8OQwrnDqMOPP8KFKcKxcMOzw59RwrrCgsKgw5paX03CvCnCvE3Ct3jCjStjORhBUXvDpQjCi8KZYjRYw53Dm8KZJ2QUw63DhV7CkFQ6EcKOwpDDvMO7wrHCh8OhPMO1XMKfFMO6w6/CuV7DhMKyw4AYw7sKKQwtcsKRwqUAaiUgWsOxwpFCwo5rIcOVwrHDik7CtMK4EsOQAiwvw7YsU8K2w4fCuTvDicK/HMOww7vDtTHDvlbCnTJF','woA9wrEqwoDCoA==','w6/CtsOZ','w7vCqsOqwp/Cmg==','LR7CpMKBwrfCtC98eMO9woIJf28ewp3CtmDCkwjCqMKiP8O9CkQ1w7Aew7M=','ZcKDPQ==','OcOINDDDrcK2DDBtbMKEwplzTcOfC8KWSWPCi1M4w5JewqjCpsO8e8OPw5pcw4coGWPCkATCl8Oowp3CoG4K','w60cHVHCjcKFVDXDoXF4K8K5w4U=','U8O4LcOVw6k=','UwLDoHnCow==','Bx3Dlw==','w7vCnVBOSijCnsKFw5DCu8OnbwjDvcKwPMKWOxbDqsO8Lm8rHG49wpQFw7bDsBY8XsKuGMKXwonDvsOKw4ljwpNnw4bDhCVDw6heZSsaesKtw4/DuzxhwqU3FDLDrMKow53DmcO3fjrDnS7DvFHCgcOWblA4AMKHDhwgRcOZdMKzwqPCqQvCuMO1VAHDq8OowojDsMK5HyHDkUFSwrrCqsKONMOPw4LDiwTDr8KBAXETaMKoZsOrVcOnV8OCYcKTN8KxFA7CnX1jPQXCocOycsKsw7LDgU1/JcKawpA=','f8KCPy8dKhcbw5PDisKcQC0lwoocw5UNwrDCjh7DpWzDqihywoo1I1tAw4rCvMONw4zCkgvDuMOlDcO6w4DCtMKwG8Osw5QWW8KxWsOCw4XDgixmw48Gw5XClcO4CsKlM8Krw5HDhgLDs8KyesOSD8OMwpluw5oow4/CgwbCt8KEKEfDlTJKG3nCuMKldMKtw64=','w6cEH0/CjcKGRRrDpWBgK8K1w4oEw69CBg==','fMKiA3sYAsOcwr3Du0/CmcKMaiLCgDI=','wpLCiyHDuWk=','Sg3DkSscZDbDgFnCpsOEwopGMw==','PMOIEx3Dn8O+AMOu','wpzCmz/DlETDsA==','woXCgcOlV1HDgnnCiMOCw5fDhMOLXiRPCw==','XinCpEUH','w7tVFsOEbsOBb8OAI8Ocw5LCpA==','wqbCqXc8wofDhsORw58bw6TCnSDCjMO2FMOdMcKGAGfCoE9+wpt7JMKtwrDChsKbw7wvwqzCp8O4D8Kdw4XDucOPw7Jgw5zDhsKGbm/DoidqKcOkTyzDkAluJEpxY8Oawo4ibMK1wpfDnjJlN8OdWcO4DMKeVMOpw5RnLMOkAEfCsH49YMOGG8Ouw7fCnsKgNwTDlsOVwp4Cw7p+AcOSwqnCvHrDt0zDvsKmwpvCskPCo8OOD8Ovw60JwrbDgD/Dq8Kbb3bDgHw9wr3DpsOsL1LDhsO/w6FfUMKTHmU8bVrDrHQ=','TBLDhmkaYSHDgUDCrsOFwo4HM3TDlAnCiMKIwqo4XR7CphrChgRkwrYjR0fDoGhPw7zDog==','cAPCsWEhVMKqw54rwq3DsXBQdg==','YSk9wpxM','IcOQfzhjwpbCmsKIdcKZwqPCpW/Ckh1ww4FB','wo3Cpz3Dl2U=','w6bDpMKzwpUoXw==','XhLDhF3Cvg==','PMKYwr1jw6LCgknDk8Ogw5YlbcKUw78=','OyDCpMOVbsK+JW/Cjwo6LcK7L8KZwrE=','w7LCsAvDuA==','wogkwrUnwobCpcO0PsO3wpvDisKpL8OE','acODK1dF','wprCizXDnljDscOUbw/CnA==','AsKMCBbCv8K2FMOPw4Rqwr5KBSF3eg==','w6vDv8Kg','XhwPw6fCjsKOwo7DkHhzYxMiw7ZPwocgwoxz','ScK8wpVbCkczwromw4AoGcKgwq1mU07DkMKfwpA1Wj9JwqPCkMKtX0E=','w7nCrcOZwqrClz3CosKgTkcUZMKPwptATmdL','w4PDlMKwwoQE','E8Kpw4HDlMKXw7PDnS4=','wqA6wr8=','WcO+wrPCpRUXEzbClcKCw5/DiFDDoDbCsAjCnEPDig3Dh8Kfw74Ww4/CkMKGZ8OxbcKnw7nDksKmQMKXUsKHHHNSWwHDq17CiUfCgcOawpfCvxPDi8KNw7dqSMKAwoPCmgnDh8K9WGYcw7LDlMOcBlHCojTDjQLCkw/Dm8OHTRHCsMOuwo7CiH1iXcOISQrCtGU9','LR7CpMKBw6HDrzVwYMO9wrAIYSA0woLCk0DCiA==','UTTDgWrCghY/','bsK0Eg==','cMOCwp3Dsyc=','wqs0wqHCmnjDssKyJ0nDrzTClcK8w74SQ8KTwqVbwpDCk1bDhMKywqJYOUfDrcOTwrXDh19kYkTDr8OjZcOQwqHCnXXDpG7Co8ORwoHCnD1Ew7U=','wo7Ci8O2','w5zCozfCuVRVXXLDjcO7Z0Q1w7/CkiNDFw==','c8KRNG0=','YMO7KEFswpTCo8O2w50Gw43DmsKBUcK1OsK2w4lbwrjChMOWcCLCosKLSsOGbko=','wrLDjsKsQsKSw4vCoCPDpXDCrsO+LMOWw6jDq1IYY8ObwobDisK5LMKmKStsw5LDqMOkHsKFKcOEA1xFMcO6wrEIw4Rhw7ZZw5cowoDDvMOBNMOww5XDo8OTwqU5w5LCicOuw43DncOpOgPDvjsJKMKGwrd8wrHDr3FMw6U3E1g4w4DComjDuAxCHUDCmGzCgngfwonClMKU','wr7CnsO9w6oRwpQjwrTCtmbDjnBQwrHCsMOEwo3DgsOCw77DsgvClMK+wpJ2Slk2w4nDj214w5A=','w4DCicOtwqTCjQ==','w4XCviY=','woPCmXRewr4=','YipLw6zCjcKcw5TDiGtod0kcw6cTwqYrwoc4wpDCtA==','EMOoMQXDtg==','V8OrXmhZ','wrnCrRV6PxXCgT4lJcO1GMOvXzokw6daw5w=','HsKww4vDisKew73Dkg==','dcOdRm1Ww4TCpQ==','ccOZXlI=','wqTCncKqw4TCv3YJIcObwoDClcO/wofDgkE7wqAnwrlXUcKRCMKLw6UWP28eJkQqw4t9HXga','w6XDv8KowpguVmI=','w5jCribCn1J2THnDqMONWVYmw6nClThUCg==','KsO4ZC1H','wqPCumZzw4jDhcOVw58Gw7PDmjzCgcKsN8KWO8KeAm7DnE18wpZxJw==','w6jClsKmwp7CrSgQL8OKwoTCjMKgw53DgFp8wqYywq0YYMOaHsKaw7Q=','csKqB0MPGsOKwqfDgk/Cg8KEYik=','wr90w4o=','w5LCqzPCkFxITGXDqsOJX2s7w7I=','NMKNDhDCo8KxD8Olw4tIwr5dIWh2Zz4dwqkNcsKicFldecOvQ8O3AcOiFcOKU8O9wqvDp8O+wqUsHw==','wprCjCTDi0PDvcOVOh/CnsOSwpvDisOFXXHDqsO4WW3DhMKPICR7C0BYwoHDuT3DmsOOw7lUUcO3w4fCrsKSbMKjVDTCu3rDgcOofVIiw6HCuB7DlmVHwpPCoB54w7UTPnHDnB/DmATCtiXDsArCqlIXNUzCihAzw6k+w6fDv8OAwoPCqcOLCFrCvlBzXj3DjwpdwqHCu2plfsOuwpjDjcOBBMKVFcKPwpXDq8Omw5gCNW4zDMKmwrPCjcOHw5DClALCtB3CkMOCw4bCkwrCvsKPwrtUbUfClgXChjHDgAgIb8KVwpDCsGXCo8KGAsOewqvDjntAw6RSwo7CrsOLw7BXw6jClCrClxnCjMOXw484E8KBbcOOwownVAzDjjHCqsONwrTCvsO8wrFELcOpwqZEZ2Zbw6XCmMKPRCdDw71hw59cwpNNGlXCu8KXQsKOLcOhJzcfDsKYw6MsaMKIOQ==','wqrDtsKkJ8Kd','w7nCqsORw6fCnQDCo8KgV0sCHsKSwpxUFGFAwrN3w4DChcKtX8OawozCgMKBw47DsMKMw5XDjsOwFcKOw6LCpMKmw7TCtjTCmS4WSsKwwrPCoDU6I8OHbsKaw5DDhRZ2w5p8FsO7w7FuWQ7Cg8OSR8KbM8OVwofCtsO1OsKsw4rCkwbDjcKHWMKdKcOkYMK4w4HDkG3DnB7DisOLHXxLMAEKw7EQSRjDlmxYO1rDmMOkR3ptDsO/w6rDgMO5wp7CssOtIWvDmMOUBw5kwqbClMK0woUMw5Q/w7djw7zCn14Qwr/DimDCm8KHw59awoRVw5jCpMKAw6XCtcOsSiHCvQ==','F8Kww4DDgsK2','wpLCl3pzwpDDl8Kew48Ww6LDhjrCkcO2HcOdO8KOAn/CoHQnw4InCcK8w6zCtsKXw7wnwrrCqsO4IsOS','GA/DhMOzd8K4w4JCwqrChjokw6vCqxnDnQ==','f8KGPXMJMAIW','cADCtQ==','VBMJwoNRbTZt','acO7Mkw=','UcOpwqDDmwwBDD7CnsKUwr/DgUnDsQ==','VHPDmMOzw5fCnUUHZE5Zw6NJw5/Cq8OUwrnCmWRlwozDsHPDqcKXN1APMj8OeR/DuGzCpsO3wq3DmcKJV8KNw7HCucOiwqRZwrM=','wosowrMqw43CvcOlOcOvw5TDssKpM8Oe','OThY','wq3Cr8O/QVw=','wr7DncKnAA==','w6jCp8KOOQPDnsOpw7/Ci8OIw4N1wqAHDRQzwq9OwoEtTMKtK3UhwrfDscKWwqbDoD3CpcKAw4trw5HCpg==','wo/ChcO0wo8J','c8KfNS8EMQcAw5DDkMOWBjYjwp0cw6QHwq/CiBjDpGHDij0uwr8qJQZHw6LCqcOww57CmT3DpcOPC8Ovw5g=','w4Mmw5M=','ccKfwpIUD1dowqgxw4YoG8OnwrZ0FVnDgcKdw4R1bX5Zw7zCvMK9Q1HCqVxnw5EJw5FiGw==','woXCnj1vMA==','w7TDpMKmwoAiVCzCq8OlP8OYwqNLw7BrVsOGGcK8TlTCh8KzwrxGTVBhw5VEw7NJw5/DtsKjw6EICcK0w5l3JCTCisOdASMiwp4pwq5HFx/CgkElODNcBMOPw7/CjcOSwo/Ch3HDv2vChCMnJkh/w5LDmkd2wptBw58BMSrDhcO5OAFTCsKTwq4eZcKwwpDDnHIyTMKZwqsZQcO9wprDqMKcwq5bw6Jiw7vClcKRw7xsSSrCs8K6w5fCssKEc8K8wokbwqHCmicmw43DgsK5w6TDucOUw67DtQLCj1rCkR9Xw4XDtF/DvcOieT7CmcOWZG/DrjBEw6dDK8KGS8Kjw4XDj8O6w73DkiXCnsOrajTDtRbDkD7DlMO7wq7DqMONQm5GGsKiNV5twqYkY07DkcOWFnFWwqnCp8Otw7vDrcO2V8OPwq7DpzfCjMKtBn/DisOuYcKgXj0nwoXCij5pw5FKw6o0QMOgw75zwq0JGcOowrXDiXnDgwPDpcKQw7PCnMK6','JcOaZlNvwojCisKDScKjwovDjWHCgw4xw5BXwrrDhS1lAADCt8KeOyzDtxJPwo7CgAdmwqzDulbCnx10bHFyC8OPwqfCpDViZsKtJsKDwq7DoFrCt1I9K8OJNsOPwpxZM2hmw6UtHX8jw60sb8KMDXDDgsO9LsKEeVV6w5hRw4fDmwl1asOjw43CocOAw5lDO1fCjcOWw7dGVCZnwofDk8OLw4smWHJrf8OXw68FPcO2c8KvwpA1w70HdcORUMKPcTQzw5DCrMK6w6xEUcKtNsOzDMKpwrVZDjhHIcKYw4kWDMKwTcKBI25gScOqw4fCrsO2w4zCu2LCm8KFwrA4wr91w6TDisOvRzlEwpTDj8OVfsOVMcKtwrrDlsO7BT8eY1s=','w6XCuMKZ','UnLDlMK/w5rClnEAaUtUwq5tw4jCtcKqwrPCmGR/woHDpUjDoMKTeHcOASMIeBHDtWzCrcOGwq3DpcKXU8Kg','di/Cn8KREV3DiMOJCADDtj7DiXtPwoLDrcOhCMK7w7xlwoQgJcKHEVtUw6A9w6XDucOjwqdOT1oSwrbCkUHCjsKTN2jCqHbChsKXw6rCnQoWEEbCtMOYw5DDjsKDWsOQesK8czY+w6LCucOewp7ClMKMwotBw5QVMFTDnyVEw4/CusOGDF57QcOVwoZLH13ChiRl','DMKIw5AyCg==','w5HCpzXCnUELR27DqsKGWHE4wrLCtR99PV9AJ8OBZSM=','w7nCpMOQwqU=','EQbDl8OxMsKHw4ZewrvDjRsEw6vCtwTDjsKZwoQ=','wo4KwpEPwrU=','FcKyw5TDisKew7HDmSTDkAvDucOTFh0=','PMKGwp5qw6TCml7DmA==','w7YFw47DucKGLQFvwrDCrcOeH8Kfwr5kw4nDvBgubsK9w4vDlU7CqMKewpDCqcKiLsKNVUd+wqLChcKO','ccKbwqh4','w48mw4vDtMKVLUE=','w4jCtSU=','WsOjwrXDrw==','w57CjRPCplI=','w63DscKxwpUzGWLCuMO+eMOPw7BEwrFVK8OrJMK3X1DCi8Kjw5RIXEMgw4RSwrNJw5nDsMKJw4IwdMKLw5Z9NTbCo8OOD3kuwoQ1w6ULLhvChlY4ZAlLBcKSw7jCm8OawonCvkPDhVnCk24iJkgXw4DDil83wrENw4gRRS/DkcO5bCRdC8KZwqwfNg==','KsKQw4wtKQ==','w6rCtsKSNgTDk8Ovw6Y=','RsK8wp8=','cT82w7LCisKGw47Dmw==','wqV1w4jChmMbQVBsw54hJcOsAlRCw4jDvQPDi2zCicK2TcKySsOxFcKEwpEEwroCwqMlwqsewpYqw7vChcKvw6rCu8K6w6MmScKZW3UHJifCpsONw6rCscOdCy3Cr0zCi07Do3Z3w65OwqnCow==','U8Onw7h4BA==','e8OVQlJww6rCjcOPwr/DgjfDtcOpwpY=','wqtpw4vDmm0CRB1zw55tJsOxAgo7w47DsD/DgDHClcKwU8O/fMOwPMKmwosCwqIT','VsK8H2Yp','AjPChsKQwqo=','YMO7KEFswpTCo8O2w50Gw43DmsKBUcK1OsK2w4lRwqjCnMOaawo=','wrDCthwwEwrChCghJcKZCcOaTy54w7JIw51YJXXCp8ODPnsvw4rDlRDDr8Oiw7w=','wrYswqcdworCrcOmE8OvwpPDm8KuNMKKw7HCmE/DswLDgHXCrMOxWMKRwq9NQ1PDn8KQcsO+SsOnw5zCpMKMRFsnwrfCiQI=','GD3DtsOfKg==','wqXCsB/DtMK/','CMKxw7sMBA==','wqjDj8Ku','KA3CtcOOwq7DqiB6ZMK2w58SZzU9w6DCmVzCkgXDrcK0OMOXRXQSw5M/w6UaShPDusOaw73CrnfCvcOHasKQEHrCocKyMxrCvsOmXjfCkT7CmSXDvn/DiC3DoMOdw4NVwqzDgMKqw5IawqbClX4=','VwoL','Sz/DkVzCiTUKwrvCiSDChcKfQcKQw43DuMKTwpjCsg==','w5g6w4E=','cw/Ct2xqVcKuw544w6LDlm1NccOmw6Q=','w6TDuMOpwpYkTmnDs8OiIsOIw7NLw7NvHcOJA8K5Ul/CnMK4w7tNU14twphIw7JUw5LCqsKpw6IQCcKlw5dlJDDChMOMGFskwoQlwqtFIQjDnkElOA5ICMKUwqzCgsOXw5HCjD7DpWvCkmpnEEgjw4jDh0x/w69Uw4hjKTLDkcO8LWJeBcKcwq5VRMOowojDlGo0WcONw6JaE8OUwr/DtsKEw7ZZwql8w67DlcKuw6cPBBvCv8Kmw4TDoMOFNsO/w48NwqvCrhZvw4bDjcOowqzDt8OPw67DqEXDiWbCigZbw4/Csg==','JB7CvsKM','RcK3MVkL','woPCisO1dlHDiHbDg8OFw7TDhsOdWCJJC2V7wp3DgzIywoopJjPCvkjDk8Oiw4AtwqvDgnAKwpBRUcKlAhR9wrVtJcOdRcK2w4QrwrQyUFY=','MMOXw4Y=','wrR3w7p9PCtINMKhw64ew5fDtgsGY8KCwo3Co8K0Nzta','wq7Cn8K3wobCqWoZLsOdwoTClMOmw4bDgw==','w6PCtsKIOx7CnMOiw6jCnsKSw590w6JBKjMPwowBwqMlT8KmNn40wpA=','wqLCnMKywofCqXUdNMOMwqDCmMO/w4bDn14g','D8KHCw==','wrjCvlV0BwnCkT87NcOEFcODXnAfw696w5pLLU/DvcO4Bmo4w7PDgCrDocO8w7s0w50wQcKwwq/DozLDiMKmwrDDrA==','Q8O/wrE=','wosowrMqw43CpMOwPsOkw5TDrcK0MsODw7DCkQ==','HcORM0TDuQ==','NsOQFw3Dn8O0McO/wrDCv2LDosKNw4s4fF86w6dzAcODw69WwqlfE8KVJcO8LsOmw5YfwpA7KUI8TcK0w5rDgsOhQ8OPw4fDsQ==','LR7CpMKBw6HDtiB3a8O9wqIOYSgjwqk=','djbCkMOLBkXCicKIKgLDiyfCk2VpwprCqsOHFMKgwrZCwoUMJ8KEHUc=','w73CgkcMTjXCmsKIw4zCv8K9bhPDpw==','NcOVw5Fhw6rCtcO1w7kuOcOmwql/bQ==','w7fCh1IDQAvCmsKUw47Cu8K7Uw7DvMKzZsKaMg==','eB06wolN','wrZxw7tsOBZeb8Kxw7UVw4fDgQAROMKzwpHCpMK2Nw==','woPCkz3DkUfDpMOVdAHCnsOQwpPCi8Oe','NsOIPg==','wrxTFsOQ','UHPDncOjw5TCkXFcaEdEwq5qw47CpsO0w77Crz88w5vDg0PDpcKUIm4dGjEbUALDnnXCvsOGwrHDn8KTTMKiw6w=','DMKDBDLCvsKkS8KIw45Pwq9WJyZ4ZUIMwqYbNcKbfnV3eMOpQsOkTMOwIsOcAcO8wqLDocOwwrJmSHPDvMOIM8OCEQrCucK5wpPDqGLCgEBHKsOrfx0kPSZXwpPCsiLCjcK9wqHCiHBeD8Oswoo9wqbCvMO4CFo+wqoHw50Rw5XCt0gvw57CljZrwqBuw6d8TcKpw6EIVCzDhcKtM3Erw742wqnDqcOkYQ==','XsOAK8Oew5Z3wqANYC42wr7DrMKUwqrCuMKjDmRyw5HCvT99w4HDkgwpwq4H','w7dLFsOVacO6fMOR','ZsO4w6k=','MMOGL3/CosKtHCtuK8Ksw55vSw==','wrHDk8Ks','dQHCpg==','w6DCusKONgPDn8Opw6PCnsOdw5huw6EB','wrHCnsO3','w7DCpMOKwqjDkgLCpsK8Xww1RMKPwoddXQ==','w4nCozPCkFhGTA==','M8OOw4R/w6PCt8Oxw7M=','XjMUwqNN','McK7FDzCjg==','OsKDwqh9w6vCgE3DmQ==','wr7DlMKuD8Kaw7DCtz7DvnzCqcOHcMONw77Dqxk/','Z8OLVw==','wo0mwqI=','w5ElWsOPwrAvwo88w4DDrcK1w5TDvA7DsSDDpMOUwqR2wqs3IB8nTsO3HsORwq48wpLCjcKKwqgWwpQ=','a8O0OlJ7w5PCqcK9w4hYw47Ch8KsD8KWBcKTw7lVwq/ChsOcag==','wo9bw7jClXzCjyfDnMKZKUPDp2PCuAXDnQ==','wqBqw7s=','QhDDkSQcZTbDnEzCs8OfwpEHNWXDlFfCuMOUwpU/VgTCihrChjl4wqZ0cX/DqGpKw77DtcOF','w6rCv8KbOQ3DpsO+w7jCmcOIw4lj','wrPCqxw=','w50/WMKCwrgUwpghw5vDocKyw63CoBXDpyDCr8Oz','NsKdwqhsw6w=','Uggcwp1YbzJndwIyw4RhKQ==','wrXCpQ1/ClTCiy49bsOEEsOCFQZjwrIQw7tLPXXDvcOGK2E+w73DkRA=','w5zDnMKtwpU9ViLCrsOvNcOJw7FBw6t/VsOEEsKqSBXCtsOiwqIQfFI9w4JCw7tTw5/DpcKuw7RH','wrPCmsKiwoQ=','w6geCg==','w5jCribCn1JxW37DrcOcTmYGw7nChTlDHVNFMQ==','w5rCqCfCjlZMTSXDv8OYWywVw7/CkiVHB05KAMOAeChrUQ==','wrTDksKvCcKJw6zCtA==','EQHDn8K8KsKlw5ZNwr/DjRwgwrfCrRzDksKIwpR8K8Ohw5/DqnhPM8OawpPDhsKsw615wql4w7LCvWLCq8OlKRJyfcORNMKGw67DosOmd8KxYQ9bWl06PMO/w7HCucOUwqDDlhYEK8OyK8OcVsOxZT3Cl8KIwpjCoEARwp3Cn1zCo8KIVFYTfcOZwrF9fw==','Y8O6w753CcKJwp/Dr0ZeSsOrfWI=','w7LCrRDDr8K/w5M3cDnDgA==','c8KfNS8WLhYTw43DnMOHWHc+wpFaw7McwrHDlTjDs2rDjiBmwoI7MQFWw7/CocOww5HCmyrCucOlDsO6w5fCmcO5X8Oyw5oSQ8K3EcOJw4rDk2NNwpUaw5nCj8KxXsO6f8Kiw6jDtDjDgcOzbMONA8OUw7FDw5Avw5TDllbCsMOaaUPClCkFHH3CssKqMA==','IAXDvMKKwrrDqTVtfsKmwoIOfiRjwobCn1/CjA7CsQ==','w7DCpMOKwqjChEDCqcK3TAwVQ8KRw4B7VXFbwrNlw47CksKCSsOcw4vCssKaw57DsQ==','WAQAwp0=','RsO+wrvDqAgHAw==','LsKVw6/DtsKO','OiVYOcOFw4PDqlJZDAEQT8KZwqtsw5fCo8OLMcOtHMOJLcOTV8O8wpXCrCtQXVbCsQfCtcKWXmDDlMK1w4bCpsOnKU8swqTCr8KBwprDvF7DsMK/w6fCo8OeJlcxw47CgMKpT8KtOMODwrPCpTtxw5YsJ8KsOsKdCsOXU8OXw4Mlw5FEWCxywrlqw6HDkU0pwoIKOg==','wo1Aw7/CqmnCig==','wqQ0wqs=','w6HCiS4Twp8=','CcKsw4E=','M8OGbg==','wrDCgVIX','w7fDg8Onw5XDsydDf8KJwqfDmcKvwpnCnwpsw6tzw7ZGMsKyQ8Ofw49AdjxMchQ8woJXRQ==','w5PClzTDhMKG','XmvDnMOjw5fCl3QW','EMKww4M=','w5rCqCfCjlZMTSXDrcONSHcmw7XCkjUfAF9HesOLZSNsXG9tflsgPXcPw6XDn33DkMK0woxow4wCw4NeRsOyTn5cwrnCnsKCdxvDvjRIwq/CjlsEJcKew4fDh8OIMsKrXsKUZUjCvW0Iw4csOHgBw705GXQQI8OIFGvDmH8jw7VL','wp1Gw7DCtHzChSjCl8KaCkDDr37Co1nDs8K4w4MJw5/CukYlAsOEw4HDmSM=','U8O0X3N7','QAjDhCQS','UQQawpATbjZnZE0Vw5l8LirCmw==','w6jDoMKiwpoIW23CrsO5EMOVw69N','XXjDl8O2w4/CkA==','NcORPGzDoMK3CSY=','w4PCvzYAwr4iw77DosKUQMKqQHUow4bDmVzCtw==','wpZJw6LCpz3CgC3Dl8KKQXHDsGXCvhnDgw==','w6w1wqnDgw==','H8K3worDhMKUw6XDmWTDjB7DucOKGh/DnsKGwrLDvEzCrcK4FxzDklnCoMKgw453ARA2w7xCw6TDn24vPzXCsDfCrhUrHBfCqsKIwpA1w5jCrsKNNsK6wqt3TcO1H0bCrW3DrsKFTH46dMK3wqDCicKDb1vDmcO7WXxFWGjCpV1Kw43Ct37DqWRAw4rDlUTCnMKdcsK9FcO7w57CqQPCmMKSGsKXFcKZSwLDmMKvw5zCgB7DqcKmWn87w7sxw5I8w5IZJwrClxh3w5jDt8OhNjDDq8O1wqXDk3PCqDhZwpowCAk=','w4cow5LDucOeP0p/wqDCvMOCGcKPw6RWwoLDpi4odcOhw7Y=','W8OXOMONw4I2wq8M','MTzCtsKWasK8JXjCgiJ7IcK6N8KMw6wuccK7MynDnsKaw6LCplFYY0HClsOkNsO+PsOiw77DtsOiPMO5VcOTIMKzwofCpcKyP23DjcOxCmJYwoIgecKOwpPCtcOlw4LDkcO3KQ==','D8Krw4XDksKSw7/CnDzDiwPDqcKaGhzDmsONwr3DpknCscKzDBfClVLCvsKuwoI6DRErw7Eew67DnHYvLjvCojfCuhs6BW/CrMKSwpwww5rCmMKaasK6wqt3cMOhEx3DvmLDq8ObRzEgdMKhw6nDicK1bwfDg8OmUnUxTX/Dh0VSw5nCsmvCimlPw4XDlQ/CvcOFasK1DcO9w4vDvUrDm8OAM8KyC8KBEwDCk8Kxw4nDgCHDssOFF043w6ciwoB9wpdaYRzCnSxGwpHDvMOuZ3jDpcOuwqXDjjTDrgRCwoM8Ak/DhcOpLMOrEyLCqwkYCVdew4zCuMKXwrrCusOZe2dJPz1Qw78DDcO/CGRgw5ITVy5owrlyKCQVIcOyWk/Cl3wMGcK6wrvCo33CqGYoWcK0ZwXDuR7DgsO4dMONBcKxGMKRwpxQV8KGw50PT8K/SgNqwrjCscKLAsOPwprCnhQdd8O5PUrCksKUC8KYdwFlRg==','MMOGL3/CosK0CSxlK8Kzw4NuVsOWQg==','XcOMLcOTw4s0wqsGYGExwqTDr8OU','wrLCgcKi','UBPDjRQWajjDi1nCgcORwoBdMmPDmQ==','wp/CqCbCiw==','ecOtRn1H','RsOcwqzDjyY=','WzvDiWPCmDg6wr8=','TBbDhDUVZjLDig==','G8KlworDjMKOw6/DiD7Dlh/DvsOOFBbCmcKrwrnDpF3CpsKu','wqbCnMKjwpjCo24YbsOewoDCgsOkw4DDmQQEwrExwp8Qd8KD','w4PCilU2Qj3CiMKlw5TCt8KsaQjCqcKvfMKtMwXDo8K7O2YmPHI7w5UzwrPDoRY6EsKyP8KMwpbDq8OtwoQ=','woI8wrc5wobCpsOlEcOzworDksKpI8OLw6rCn3LDuA==','OcOfAA7Di8K/D8OvwqbDvXjDssKqwoAJWEIkw7pPPcOow65AwrdQBcKFF8O6M8OEwpcPwpQjNV86SsKuw7rDgcOnfcOFw4fDqxXCvcKIw7bDm8K2wr7DkMOFw7w8wqs9wrcoLmAMw7M2SHnDs3vDvsO/Fx7CrUXCl8Klw4bCucOHXMKSw6DDsMKbwobCqMKbG8OGw7UQBCcH','wo3CmznDrUPDqsObexLCmsOqwpvCicOV','w7RSFA==','w7JcBcOGK8O5fMObN8KGw6TCtMKfQ07DoQ==','cTlaYA==','wqbCtUJ3woXDk8OZw4oWw6XDtjrCisOtFg==','P8OXw4wjw7zCqcOlw7YoPcOnwrA+bD15YlkHM8OVdkDDrFMlQRtQXMOaKg==','Zj8IwqjCi8Kew5XDnWt4cU1fw61WwoY6wp84w5vChVrCpcO9EFENw5TCjAlNRMOiMMKrw6J5','w4Mow4nDvQ==','wqTCncKqw4TCoGIaNMOMwovClMOqw5vCg048wqEqwqAX','Kz3CtA==','w6jCtAHDscKuw40FdznDicKWCMONw4M=','ZjEJw6o=','QnjDjcOCw4jClEYdZUlVw7REw5vCscOwwr/ChXM=','wobCkSo=','WsOjwrM=','w6DCiinCnU9EB3jDu8OLXnA9w6jCn2JSC0hHesOwP30zdm0xRFcyI3scw7rDqSM=','Y8OzDMOow6c=','w67CrxTDr8Knw48BfQ==','LMOUfRx2w4jCgMKUUsOkwpzCkGLDnyFww4BMwrrDlyNyLxXCscOZCTfDpxM=','KxDCtQ==','wqBmw5nDiXpFTlZwwpV8PsO0WHAew57DoRnDqCPClMKkRsO5a8OkOMK0wooIwqMew7kEwq8JwrY3w7zCj8O1w4DCv8Kgw5EuT8KATSloZzPCs8ORwrjCoMKaTWbCvUnDmQ==','RBrCjy0MeifDml/CssODwpdEOD/DqUrCmMKPwrczcR7CkDjChApjwqdPakHDvWpKw7TDtcOhI8KEwo/DnS7DsBw=','wrl2w7k=','5pmb5o64NjfCpiHDvsO2wqZ9wodhcTnDgeWNtOaVnuWnv+i2gl8=','I8KZw7U+UC9gSRDDlCEtwq0o','FsK+w5LDh8KDwrLDki/DkETDvsOJFV3Dv8KXwqjDuF7ClsKOKTDDlFPCosKsw48tCxA2wrwfw7LDmEZkGDbCti/CqDwnGzXCocKbwpQ2w6fCrsKNccKqwqdgVsK7QQTDq3HDrsKLAz4zYcO3wrTDlMK8NWHDn8OmSjUIFSrCqHRSw4XCp3bConoGwo3Ckh3CrsKaIMK8FMOzwo7Dq0vCmQ==','wo3CksO0dlLDjnPCiQ==','woXCjCrCk1rDvMOEcxnCjMKKwpLCkMOEWTbDlMKzZHHDksKIdBd1FA9Uwpw=','wqZow4g=','ZcOsO1J4w5XCrMO3','wqgMU8KYOsK1IsKKcMOqwo7DoMOdGADCuSJCw5t1YTkrwoTCmQnDn8KPJXokw5rDkWRa','XMOtwqLDqhVMCTrCjcOfwoLDk0jCug7Dqi7CiUHDqjrDuMKow4EZw5PClMKIYMK2ccKswqPDqcKsR8KrU8KVBXhlUxbDlF3CkkvClcKbw5XCp1rCmsKGw64yR8Kdw5nDmh/DgMO/BV0fwq/DqMOdFEjCqQPDhRXCrAzDgMOLWV/DvMK8w57DgC41VMOUBgnCvm53VBXCmg==','dMKxEl0GGMOOwq0=','fMKqI0QZ','wqVdw6hEPQ==','w7nCmMO+wq0E','XsOAK8Oew5Z3wqANYC42wr7DrMKUwrHChMKcKWVww5fCvR1ew5LDmB4vwrkM','ThYJ','w7PCqMOMwqXCmQPCosK8TEMSWcKSwoA=','CcKJGifDpMK4GcOIw4APwohHJyF3bg==','wq8+wrjDp0jDgMKK','Xm/DnsK/w5rCiHQRbkcew6h2w47CosKqwrnCmnpgw4zDtF3DucKCOFdSMDUaVAXDt3nCgsOXwqvDnMK5T8Klw7rCtMO+w6UPwqnCt2QBEcKdTsKCw6F4QHMpNcOPwrvDusOuwqXCinARwrAqw5Jlw4vCkcOUwonDpiFxTMObd8OxRk/CocKZw7QPesKXIcOdw4o2wowOw4Qt','dcOdRn9lw7fChMOIwqjDgjfDtcOpwpbCtsOQwpDCqyZ/w50=','UcOqDcOlw4U=','w7DCpMOKwqjChEDCqcK3TAwVQ8KRw4B7TnZfwq5Rw7HCu8KXQMOAw4zCscKQw4/DqsKXw7Y=','wo3CmznDuE/DucOEYybCrMOowrzChcOTXXfDtMKv','wrlzw47DnGsIAEVrw5JrbcO7GUlCw4rDvAnDly3Ck8KhD8Oza8OFd8K0wpEJwqIEwqUawroJw4wVw6XCncO1w6vCscK8w51nScKaW2IqFCHCoMOUw73CusKhUHjCq1nCnQrCoz5yw6RdwqzDusKPb8Obwqwhw4cRFsKOL3fClMK+XF9nOsOwbMOcw5HCvy7Cp0o5f8OLwrDCgcK9GcOLY8OeYMOnLRPDjE/Dtzttw5rDiMK0PDcUN8Ouwq9tw64bR8OUw78FfMOEwrXCh8OdXD3CjV7DshHCocKLXcKPLlhqw7Vdw5wSw4NSwo9nw4/DuMOBP8KeGMKSYCI2HsK7HsOHwrwdAhsnZMKJIMKxCj8iwqN3Z8OtwqNmwpFhLsKnXsOpccK2wpt5w7oYdhnDjsOMeAzCisOewrRAQsKLw7bDhFg7JQ==','ZcO8w6ZvGMKUw4nCr1tRSsOnYGJZPX7CpFzDtSXCg8KtZUZqQcKiw4DCtsOcITfDjMKuIkk6wpY=','M8OKKXLDqcK1DSx2ZMKUw55zUQ==','WcO+wrPCpRUXEzbClcKCw5/DiFDDoDbCsAjCnEPDig3Dh8Kfw74Ww4/CkMKGZw==','esOcendt','XhjDtGjCjw==','DMKeCTTCpsK7GcOC','w7osw4bDjsKZKVhfwrnCp8OOA8KCw6pywonDjRg/f8O6w6XChRrDgsKuwpnCnsKkNcKETgRowrfCk8KVw4RlAMKtwrg8LQ==','5puv5o6SLHMQLBcfw57Dl8OTTzwjwonlj7Dml7flpZnot6TDmg==','wrjCtAbDrVc=','w5TCtCTDkkFQXWLDssObBWogw6jClmJjC0tGMcObfh1rR2kuQxAnL2w1w6HDv2zDncKgwpNkw64ew6VFVcOoX0EVw7DClcKEZAjCqHlOwq/CmR4nP8KcwprCp8KAK8K+RsKUJljCn2EWwoAGOG4Hw705GXQQI8OIFGvDmH8jw7VL','w5fCqSQ=','PMOMEUHDksOhAMOpwrrCtiXDqcKyw5oxAlU7w6d0QcOXw55Dw7dtM8KqMMO8P8OBw5wIwrc2HkQmTMK5','Tw/Dhg==','w7jCilkHXzA=','wo4/wqA5wo/Cp8OwNA==','w4PCvGMEbA=='];var _0x487b=function(_0x598a05,_0x487b88){_0x598a05=_0x598a05-0x0;var _0x40a7da=_0x598a[_0x598a05];if(_0x487b['xudWRe']===undefined){(function(){var _0x4d084b;try{var _0x54505d=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x4d084b=_0x54505d();}catch(_0x30eee1){_0x4d084b=window;}var _0x1854f2='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x4d084b['atob']||(_0x4d084b['atob']=function(_0x175d4a){var _0x4aab16=String(_0x175d4a)['replace'](/=+$/,'');var _0x184ce9='';for(var _0x40742f=0x0,_0x36238,_0x4b4428,_0x1901cd=0x0;_0x4b4428=_0x4aab16['charAt'](_0x1901cd++);~_0x4b4428&&(_0x36238=_0x40742f%0x4?_0x36238*0x40+_0x4b4428:_0x4b4428,_0x40742f++%0x4)?_0x184ce9+=String['fromCharCode'](0xff&_0x36238>>(-0x2*_0x40742f&0x6)):0x0){_0x4b4428=_0x1854f2['indexOf'](_0x4b4428);}return _0x184ce9;});}());var _0x3f10d4=function(_0x3d14d7,_0x54ec98){var _0x194977=[],_0x1630b7=0x0,_0x7adfe8,_0x505a28='',_0x1057e6='';_0x3d14d7=atob(_0x3d14d7);for(var _0xcada6a=0x0,_0x310716=_0x3d14d7['length'];_0xcada6a<_0x310716;_0xcada6a++){_0x1057e6+='%'+('00'+_0x3d14d7['charCodeAt'](_0xcada6a)['toString'](0x10))['slice'](-0x2);}_0x3d14d7=decodeURIComponent(_0x1057e6);var _0x408316;for(_0x408316=0x0;_0x408316<0x100;_0x408316++){_0x194977[_0x408316]=_0x408316;}for(_0x408316=0x0;_0x408316<0x100;_0x408316++){_0x1630b7=(_0x1630b7+_0x194977[_0x408316]+_0x54ec98['charCodeAt'](_0x408316%_0x54ec98['length']))%0x100;_0x7adfe8=_0x194977[_0x408316];_0x194977[_0x408316]=_0x194977[_0x1630b7];_0x194977[_0x1630b7]=_0x7adfe8;}_0x408316=0x0;_0x1630b7=0x0;for(var _0x1df5c9=0x0;_0x1df5c9<_0x3d14d7['length'];_0x1df5c9++){_0x408316=(_0x408316+0x1)%0x100;_0x1630b7=(_0x1630b7+_0x194977[_0x408316])%0x100;_0x7adfe8=_0x194977[_0x408316];_0x194977[_0x408316]=_0x194977[_0x1630b7];_0x194977[_0x1630b7]=_0x7adfe8;_0x505a28+=String['fromCharCode'](_0x3d14d7['charCodeAt'](_0x1df5c9)^_0x194977[(_0x194977[_0x408316]+_0x194977[_0x1630b7])%0x100]);}return _0x505a28;};_0x487b['ZUfDTj']=_0x3f10d4;_0x487b['Aorjzq']={};_0x487b['xudWRe']=!![];}var _0x390ad1=_0x487b['Aorjzq'][_0x598a05];if(_0x390ad1===undefined){if(_0x487b['EsfyET']===undefined){_0x487b['EsfyET']=!![];}_0x40a7da=_0x487b['ZUfDTj'](_0x40a7da,_0x487b88);_0x487b['Aorjzq'][_0x598a05]=_0x40a7da;}else{_0x40a7da=_0x390ad1;}return _0x40a7da;};var _21k1ka0=new Set();function _kakakkkakkkkkkksanann(_0x3f5dbe){Interceptor['attach'](_0x3f5dbe,{'onEnter':function(_0x5704c4){_0x5704c4[0x1]=ptr(0x0);this['callback']=_0x5704c4[0x2];if(this[_0x487b('0x142','bVr3')]<0x1000){if('QNFVb'==='VPhFn'){console[_0x487b('0x157','6xh1')](err);}else{return;}}if(_21k1ka0['has'](this['callback']['toString']())){return;}_21k1ka0['add'](this[_0x487b('0xc5','wl9z')][_0x487b('0xc7','KYvS')]());if(bbbbbb==_0x487b('0x2c','Ewrm')){var _0x449cc4=Process[_0x487b('0x8d','IAeX')](this['callback']);if(_0x449cc4){console['log']('hahahhahah\x20|-->\x20Module:\x20'+_0x449cc4[_0x487b('0x150','MMy8')]+'\x20offset:'+ptr(_0x5704c4[0x2])['sub'](_0x449cc4['base']));}}Interceptor[_0x487b('0x50','!S%l')](this['callback'],{'onEnter':function(_0x23370c){},'onLeave':function(_0x347669){if('DDUzk'!=='ZsGHe'){_0x347669['replace'](0x0);}else{TrustManagerImplCheckTrusted_9999=TrustManagerImplClz[_0x487b('0x10b','wl9z')][_0x487b('0x9','IAeX')]('[Ljava.security.cert.X509Certificate;','java.lang.String',_0x487b('0xce','u)JU'),_0x487b('0xe1','wl9z'),_0x487b('0xbf','MMy8'));}}});},'onLeave':function(_0x4add0a){}});}function snska100110921jrn(_0x4a9659,_0x30e2f7){const _0x2ec10e=_0x4a9659[_0x487b('0x27','v^R2')]('--x');if(_0x2ec10e==null||_0x2ec10e['length']===0x0){if(_0x487b('0x4e','SQ9u')===_0x487b('0x9c','44m2')){return null;}else{var _0x4aecfe=Java[_0x487b('0x54','77jp')](_0x487b('0xea','oZWb'));var _0x4b9bda=_0x4aecfe['sslSocketFactory']['overload'](_0x487b('0x84','u)JU'));_0x4b9bda[_0x487b('0x153','&A)n')]=function(_0x105e43){var _0x4beee6=_0x4b9bda[_0x487b('0x154','KYvS')](this,Java['use']('gz.justtrustme.Helper')[_0x487b('0x11','wl9z')]());return _0x4beee6;};var _0x2c9f59=_0x4aecfe['sslSocketFactory'][_0x487b('0x79','x$&t')](_0x487b('0x53','x1X1'),'javax.net.ssl.X509TrustManager');_0x2c9f59[_0x487b('0x97','7MlP')]=function(_0x5a545f,_0x24ee93){var _0x34a887=_0x2c9f59['call'](this,Java[_0x487b('0x124','x$&t')](_0x487b('0x11b','x1X1'))['getEmptySSLFactory'](),_0x24ee93);return _0x34a887;};}}_0x2ec10e['forEach']((_0x3065ad,_0x3d2066)=>{Memory['scan'](_0x3065ad[_0x487b('0x3b','&kEb')],_0x3065ad[_0x487b('0x6f','&A)n')],_0x30e2f7,{'onMatch':function(_0x4112ba,_0x37b296){if('OiNQT'==='OiNQT'){_kakakkkakkkkkkksanann(_0x4112ba);}else{var _0x41204d=Java['use']('java.util.ArrayList');if(TrustManagerImplClz[_0x487b('0x38','l181')]){TrustManagerImplClz['checkTrustedRecursive'][_0x487b('0x6d','SQ9u')]=function(_0x2deb86,_0x4d7790,_0x416cbf,_0x432184,_0x22960a,_0x13c0c6){var _0x31fef1=_0x41204d['$new']();return _0x31fef1;};}}},'onError':function(_0x3d0127){},'onComplete':function(){}});});}function fmap1p1ppa01nmalkaar(_0xcea523){const _0x4db293=Module[_0x487b('0x29','6xh1')](_0xcea523['name'],'SSL_CTX_set_custom_verify');if(_0x4db293){_kakakkkakkkkkkksanann(_0x4db293);}else{if(_0x487b('0xd1','OYI#')!=='XgLdh'){var _0x53c208=null;var _0x1cca25=Module[_0x487b('0xe2','Ob3d')](_0xcea523['name']);for(var _0x5d2148=0x0;_0x5d2148<_0x1cca25[_0x487b('0x185','qIAu')];_0x5d2148++){if(_0x1cca25[_0x5d2148]['name'][_0x487b('0x7d','bVr3')]('set_custom_verify')!==-0x1){if('mYkkc'!=='Retiy'){_0x53c208=_0x1cca25[_0x5d2148][_0x487b('0x17','RSxm')];_kakakkkakkkkkkksanann(_0x53c208);return;}else{console[_0x487b('0x184','dF#5')]('com.squareup.okhttp.setCertificatePinner\x20was\x20hooked!');return this;}}}snska100110921jrn(_0xcea523,_0x487b('0x127','Ob3d'));}else{if(TrustManagerImplCheckServerTrusted_8813){TrustManagerImplCheckServerTrusted_8813['implementation']=function(_0x1db648,_0x4f671f,_0x2e28d0){console[_0x487b('0xc6','RSxm')]('com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27java.lang.String\x27)\x20was\x20hooked!');return newArrayList();};}}}}var _21k1ka0hahaha=new Set();function waitForModule(_0x26b510){return new Promise(_0x12cba3=>{if(_0x487b('0x159','TUxD')!=='WRQWI'){console[_0x487b('0x165','fpwA')]('okhttp3.internal.tls.OkHostnameVerifier.verify(\x27java.lang.String\x27,\x20\x27java.security.cert.X509Certificate\x27)\x20was\x20hooked!');return!![];}else{var _0x162ab6=0x0;var _0x51c0fb=Date[_0x487b('0xae','MMy8')]();var _0x6e0857=setInterval(()=>{if('CCwEn'!==_0x487b('0x6c','bVr3')){var _0x4f01b9=Process[_0x487b('0x6e','l181')]();var _0x28e351=Date['now']()-_0x51c0fb;for(var _0x1fba1c of _0x4f01b9){if(_21k1ka0hahaha[_0x487b('0x7a','QYQ)')](_0x1fba1c['name'])){continue;}if(_0x1fba1c['name'][_0x487b('0x116','44m2')](_0x26b510)!=-0x1){_21k1ka0hahaha[_0x487b('0x88','j4KY')](_0x1fba1c['name']);clearInterval(_0x6e0857);_0x12cba3(_0x1fba1c);}}_0x162ab6++;if(_0x28e351>0xc350||_0x162ab6>0xc350){if(_0x487b('0x5e','EGxG')==='xCWvF'){var _0x187e64=DefaultHttpClientClassRapeConstructor[_0x487b('0x2','j4KY')](this,Helper['getCCM'](v0,v1),v1);console['log'](_0x487b('0x65','kRt3'));return _0x187e64;}else{clearInterval(_0x6e0857);}}}else{console['log'](_0x487b('0x120','q0$6'));return netBuilder['enablePublicKeyPinningBypassForLocalTrustAnchors']['call'](this,!![]);}},0x0);}});}waitForModule(_0x7f80('xkhulvggp'))[_0x487b('0x5','a@X3')](_0x5b9e19=>{fmap1p1ppa01nmalkaar(_0x5b9e19);});waitForModule(_0x7f80(_0x487b('0x121','^qxC')))['then'](_0x28376d=>{fmap1p1ppa01nmalkaar(_0x28376d);});waitForModule(_0x7f80(_0x487b('0x72','EGxG')))[_0x487b('0x112','Ob3d')](_0x2aa70b=>{fmap1p1ppa01nmalkaar(_0x2aa70b);});function classExists(_0x14fb1c){var _0x1d0e62=![];try{if(_0x487b('0xcc','77jp')!=='FLGgL'){var _0xb9e6d4=null;var _0x2a8b42=Module['enumerateExports'](targetLibrary['name']);for(var _0x4bdccf=0x0;_0x4bdccf<_0x2a8b42[_0x487b('0x131','^HMm')];_0x4bdccf++){if(_0x2a8b42[_0x4bdccf]['name']['indexOf']('set_custom_verify')!==-0x1){_0xb9e6d4=_0x2a8b42[_0x4bdccf][_0x487b('0xe','KYvS')];_kakakkkakkkkkkksanann(_0xb9e6d4);return;}}snska100110921jrn(targetLibrary,_0x487b('0x167','&kEb'));}else{var _0x5cdff5=Java[_0x487b('0x16e','EzHt')](_0x14fb1c);_0x1d0e62=!![];}}catch(_0x28a300){}return _0x1d0e62;};function loadDexfile(_0x257bf8){Java['perform'](function(){if(_0x487b('0x1f','^qxC')===_0x487b('0x7','WlH7')){console[_0x487b('0x122','QYQ)')]('com.squareup.okhttp\x20not\x20found');}else{Java[_0x487b('0x1d','OYI#')](_0x257bf8)['load']();}});};loadDexfile(_0x487b('0x96','Ob3d'));function hasTrustManagerImpl(){return classExists(_0x487b('0xb4','^HMm'));}function newArrayList(){var _0x3e7b54=Java['use']('java.util.ArrayList');return _0x3e7b54['$new']();}function b0ringsslp(){try{if('haTsG'!=='haTsG'){if(TrustManagerImplCheckTrusted_2872){TrustManagerImplCheckTrusted_2872[_0x487b('0x5f','dF#5')]=function(_0x419c0e,_0xc28f90,_0x51f209,_0x131545,_0x31b868,_0x5d4c04){console['log']('private\x20java.util.List\x20com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');return newArrayList();};}}else{if(classExists('org.chromium.net.CronetEngine$Builder')){var _0x490b2f=Java['use'](_0x487b('0x106','ApF6'));_0x490b2f[_0x487b('0xa6','^HMm')][_0x487b('0x70','!S%l')]=function(_0xd7116c){console['log'](_0x487b('0xb5','oZWb'));return _0x490b2f[_0x487b('0xe8','TIBI')][_0x487b('0xa4','u)JU')](this,!![]);};_0x490b2f[_0x487b('0x22','q0$6')][_0x487b('0xbb','x$&t')]=function(_0x1073b7,_0x38058a,_0x278092,_0x3465d6){console[_0x487b('0xfc','4OZK')]('org.chromium.net.CronetEngine$Builder\x20addPublicKeyPins\x20was\x20hooked,\x20hostName\x20=\x20'+_0x1073b7);return this;};}}}catch(_0x2d400f){console['log'](_0x2d400f);}}function kaooqpjakk10a(){var _0x451665=Java['use']('android.webkit.WebViewClient');_0x451665['onReceivedSslError']['implementation']=function(_0x52ad90,_0x49e4e7,_0x3686a0){if('ClZRH'!==_0x487b('0x41','bVr3')){TrustManagerImplCheckTrusted_9999[_0x487b('0x1','l181')]=function(_0x458d82,_0x529439,_0x286803,_0xc410d8,_0x13572e){console[_0x487b('0x15c','x1X1')]('com.android.org.conscrypt.TrustManagerImpl.checkTrusted(\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27javax.net.ssl.SSLSession\x27,\x20\x27javax.net.ssl.SSLParameters\x27,\x20\x27boolean\x27)\x20was\x20hooked!');return newArrayList();};}else{console['log'](_0x487b('0xd0','!S%l'));_0x49e4e7['proceed']();return;}};_0x451665['onReceivedError']['overload'](_0x487b('0x40',')XWS'),_0x487b('0xa2','Ewrm'),_0x487b('0x4','5aGD'),'java.lang.String')['implementation']=function(_0x591ff3,_0x426846,_0x5e08e8,_0x5d6601){if('RNEYM'===_0x487b('0x128','&A)n')){console[_0x487b('0x157','6xh1')]('WebViewClient\x20onReceivedError\x20was\x20hooked!');return;}else{fmap1p1ppa01nmalkaar(lib);}};_0x451665['onReceivedError'][_0x487b('0xff',')XWS')]('android.webkit.WebView',_0x487b('0xcb','fpwA'),'android.webkit.WebResourceError')[_0x487b('0xf0','EGxG')]=function(){console[_0x487b('0xfc','4OZK')]('WebViewClient\x20onReceivedError\x20was\x20hooked!');return;};}function klalo1mmmmal(){if(classExists(_0x487b('0x91','Ob3d'))){if('DLgPc'!==_0x487b('0x17c','bVr3')){var _0x4e0616=Java[_0x487b('0xe5','6xh1')](_0x487b('0x14f','KYvS'));var _0x33030b=_0x4e0616['check'][_0x487b('0x1e','5aGD')](_0x487b('0xfd','v^R2'),_0x487b('0x161','c5B9'));_0x33030b[_0x487b('0xfb','wl9z')]=function(_0x5fd39a,_0x24332d){if(_0x487b('0x57','TUxD')===_0x487b('0x6a','EGxG')){console[_0x487b('0xa8','q0$6')](_0x487b('0x117','OYI#'));}else{if(!classExists('appcelerator.https.PinningTrustManager')){return;}var _0x27f3d3=Java['use']('appcelerator.https.PinningTrustManager');var _0x2b15a0=_0x27f3d3[_0x487b('0xb9','OYI#')][_0x487b('0x143','dF#5')]();_0x2b15a0['implementation']=function(){};}};}else{TrustManagerImplCheckTrusted_2872[_0x487b('0x10f','EzHt')]=function(_0x30847b,_0x5474fb,_0x3a8e2d,_0x4fc36e,_0xe8283e,_0xf4a78b){console[_0x487b('0x15c','x1X1')]('private\x20java.util.List\x20com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');return newArrayList();};}}if(classExists('okhttp3.CertificatePinner')){try{if('cYuwQ'!==_0x487b('0x71','u)JU')){TrustManagerImplCheckServerTrusted_7015=TrustManagerImplClz['checkServerTrusted']['overload'](_0x487b('0x30','7MlP'),_0x487b('0x170','WlH7'),_0x487b('0x18','oZWb'));}else{var _0x4f3d5c=Java[_0x487b('0x124','x$&t')]('okhttp3.CertificatePinner');var _0x571191=_0x4f3d5c['check']['overload']('java.lang.String',_0x487b('0xa7','!S%l'));_0x571191[_0x487b('0x67','Ewrm')]=function(_0x3bde97,_0x1e9ebc){console[_0x487b('0xf7','@vff')]('okhttp3.CertificatePinner.check(\x27java.lang.String\x27,\x20\x27java.util.List\x27)\x20was\x20hooked!');};}}catch(_0x17fe0f){}}if(classExists(_0x487b('0x178','@vff'))){try{if(_0x487b('0x52','v^R2')!==_0x487b('0xc','IutC')){var _0x552af6=Java['use']('okhttp3.internal.tls.OkHostnameVerifier');var _0x6b8303=_0x552af6['verify'][_0x487b('0xa3','EzHt')](_0x487b('0x31','RSxm'),_0x487b('0xb7','5aGD'));_0x6b8303[_0x487b('0x56','IutC')]=function(_0x19b01f,_0x136b4a){console['log']('okhttp3.internal.tls.OkHostnameVerifier.verify(\x27java.lang.String\x27,\x20\x27javax.net.ssl.SSLSession\x27)\x20was\x20hooked!');return!![];};var _0x3f4f69=_0x552af6['verify'][_0x487b('0x60','TIBI')](_0x487b('0x4','5aGD'),_0x487b('0x3e','q0$6'));_0x3f4f69[_0x487b('0x70','!S%l')]=function(_0x13616e,_0x2c428e){if(_0x487b('0xdc','77jp')!=='UGiXn'){com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651['implementation']=function(_0x282e49,_0x348a81,_0x1f7863,_0x444118){console[_0x487b('0xde',')XWS')]('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');};}else{console['log'](_0x487b('0xf4','WlH7'));return!![];}};}else{if(classExists(_0x487b('0x3f','4OZK'))){var _0x38b88d=Java['use']('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');var _0x276c0a=_0x38b88d['verify'][_0x487b('0x169','7MlP')](_0x487b('0x4','5aGD'),'[Ljava.lang.String;','[Ljava.lang.String;',_0x487b('0x8e','x$&t'));_0x276c0a['implementation']=function(_0x54524e,_0x5cfae1,_0x478be1,_0x24aedb){console['log'](_0x487b('0xda','w5gP'));};}}}catch(_0x1356aa){}}if(classExists('okhttp3.OkHttpClient$Builder')){if(_0x487b('0x2a','77jp')==='etAMZ'){console[_0x487b('0x8','^qxC')]('com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27javax.net.ssl.SSLSession\x27)\x20was\x20hooked!');return newArrayList();}else{try{if('DDwpO'!==_0x487b('0x78','w5gP')){console[_0x487b('0x12a','x$&t')](_0x487b('0x148','TIBI'));}else{var _0x34db2f=Java['use']('okhttp3.OkHttpClient$Builder');var _0x4a2840=_0x34db2f[_0x487b('0x13e','dF#5')][_0x487b('0x163','R%CU')]('javax.net.ssl.SSLSocketFactory');_0x4a2840['implementation']=function(_0x19e900){if(_0x487b('0xac','4OZK')!=='RtdKy'){console['log'](_0x487b('0xd5','x1X1'));return SSLSocketFactory[_0x487b('0x14c','q0$6')]();}else{var _0x3e8adc=_0x4a2840['call'](this,Java['use']('gz.justtrustme.Helper')['getEmptySSLFactory']());return _0x3e8adc;}};var _0x269b50=_0x34db2f[_0x487b('0x108','^qxC')][_0x487b('0x138','TUxD')]('javax.net.ssl.SSLSocketFactory','javax.net.ssl.X509TrustManager');_0x269b50['implementation']=function(_0xf897d8,_0x3625ea){if('YzgEm'!=='YzgEm'){TrustManagerImplCheckTrusted_2872=TrustManagerImplClz[_0x487b('0x24','SQ9u')]['overload'](_0x487b('0x9f','kRt3'),'[B','[B',_0x487b('0x13b','a@X3'),_0x487b('0xa0','OYI#'),'boolean');}else{var _0x580d42=_0x269b50[_0x487b('0xdb','x1X1')](this,Java[_0x487b('0x152','l181')]('gz.justtrustme.Helper')[_0x487b('0x69','T]f3')](),_0x3625ea);return _0x580d42;}};}}catch(_0x3a8114){}}}if(classExists('com.squareup.okhttp.OkHttpClient')){if('IQciX'==='IQciX'){try{if('KVhaK'!=='XoDEi'){var _0x35baf2=Java['use'](_0x487b('0x14e',')XWS'));_0x35baf2['setCertificatePinner'][_0x487b('0x99','5aGD')]=function(_0x1043f9){if(_0x487b('0x180','EGxG')!==_0x487b('0x11f','x$&t')){retval[_0x487b('0xfe','5aGD')](0x0);}else{console['log']('com.squareup.okhttp.setCertificatePinner\x20was\x20hooked!');return this;}};}else{console[_0x487b('0x15f','yDxj')]('com.android.org.conscrypt.TrustManagerImpl.checkTrusted(\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27javax.net.ssl.SSLSession\x27,\x20\x27javax.net.ssl.SSLParameters\x27,\x20\x27boolean\x27)\x20was\x20hooked!');return newArrayList();}}catch(_0x20f8fe){console[_0x487b('0xf1','a@X3')](_0x487b('0x76','RSxm'));}}else{var _0x3504bd=Java['use']('android.app.ActivityThread')[_0x487b('0x147','!S%l')]()[_0x487b('0x173','%XuC')]();var _0x30a322=_0x3504bd[_0x487b('0xa5','6xh1')]();console[_0x487b('0xf7','@vff')]('Package\x20name:\x20'+_0x30a322);if(_0x30a322[_0x487b('0x119','&A)n')](_0x7f80('qkn.gg.olmhkum'))){}else{setTimeout(oooooooooxxxxxxxxx,0x0);}}}}function xmalk11o_JKKJKK(){if(classExists('org.xutils.http.RequestParams')){var _0x1f8587=Java['use'](_0x487b('0x17a','6xh1'));var _0xfc75b=_0x1f8587[_0x487b('0x48','5aGD')]['overload']('javax.net.ssl.SSLSocketFactory');_0xfc75b['implementation']=function(_0x409240){console['log']('org.xutils.http.RequestParams.setSslSocketFactory(\x27javax.net.ssl.SSLSocketFactory\x27)\x20was\x20hooked!');var _0x858f68=Java[_0x487b('0x98','fpwA')]('gz.justtrustme.Helper');_0xfc75b[_0x487b('0x47','TIBI')](this,_0x858f68[_0x487b('0x133','j4KY')]());};var _0x5ddf96=_0x1f8587['setHostnameVerifier'][_0x487b('0x39','OYI#')](_0x487b('0x16d','TUxD'));_0x5ddf96[_0x487b('0x118','@vff')]=function(_0x40498a){if(_0x487b('0x101','WlH7')!==_0x487b('0x63','Ewrm')){console['log'](_0x487b('0x7b','6xh1'));var _0x169c69=Java['use'](_0x487b('0x15e','dF#5'));_0x5ddf96['call'](this,_0x169c69['$new']());}else{var _0x32cd3a=Java[_0x487b('0x44','TIBI')](_0x487b('0x91','Ob3d'));var _0x48c371=_0x32cd3a['check'][_0x487b('0x102','SQ9u')]('java.lang.String',_0x487b('0xf8','a@X3'));_0x48c371[_0x487b('0xbb','x$&t')]=function(_0x4fcc34,_0xc24e10){console['log'](_0x487b('0x11a','77jp'));};}};}}function xnalkak11ll0ppi0000000ppp(){if(classExists('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier')){if(_0x487b('0x68','EzHt')!==_0x487b('0x87','v^R2')){var _0x3f088a=Java['use'](_0x487b('0x164','EGxG'));var _0x30113c=_0x3f088a[_0x487b('0xd7','bVr3')]['overload'](_0x487b('0xf5','TUxD'));_0x30113c['implementation']=function(_0x37a715){console[_0x487b('0x156','EGxG')](_0x487b('0x5b','77jp'));var _0x1639c2=Java['use'](_0x487b('0x144','x$&t'));_0x30113c[_0x487b('0x90','%XuC')](this,_0x1639c2['getEmptySSLFactory']());};var _0x2ddbe0=_0x3f088a['setHostnameVerifier'][_0x487b('0x169','7MlP')]('javax.net.ssl.HostnameVerifier');_0x2ddbe0[_0x487b('0xfb','wl9z')]=function(_0x3920f6){console['log']('org.xutils.http.RequestParams.setHostnameVerifier(\x27javax.net.ssl.HostnameVerifier\x27)\x20was\x20hooked!');var _0x3dacda=Java['use'](_0x487b('0xe4','IAeX'));_0x2ddbe0[_0x487b('0x1b','&kEb')](this,_0x3dacda[_0x487b('0x135','QYQ)')]());};}else{var _0x1cef83=Java['use']('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');var _0xa035f3=_0x1cef83[_0x487b('0x61','EGxG')]['overload']('java.lang.String',_0x487b('0x75','KYvS'),_0x487b('0x0','IutC'),_0x487b('0x92','w5gP'));_0xa035f3[_0x487b('0x6d','SQ9u')]=function(_0x5c472c,_0x4a01ba,_0x28dcf3,_0xb72298){if('spNJI'===_0x487b('0x12d','%XuC')){_21k1ka0hahaha[_0x487b('0x4c','u)JU')](module['name']);clearInterval(interval);resolve(module);}else{console[_0x487b('0x14a','&kEb')](_0x487b('0x136','x$&t'));}};}}}function xxxxkakjakkkk(){if(!classExists(_0x487b('0x86','4OZK'))){return;}var _0x9cef09=Java['use']('com.android.org.conscrypt.Platform');var _0x5ad300=undefined;try{_0x5ad300=_0x9cef09[_0x487b('0x10d','ApF6')][_0x487b('0x129','^HMm')]('javax.net.ssl.X509TrustManager',_0x487b('0x158','5aGD'),_0x487b('0x35','ApF6'),'com.android.org.conscrypt.OpenSSLEngineImpl');}catch(_0x51b937){}finally{if(_0x5ad300){if('VQNIf'==='lkXMV'){console['log']('hahahhahah\x20|-->\x20Module:\x20'+find_module['name']+_0x487b('0xf','77jp')+ptr(args[0x2])['sub'](find_module['base']));}else{_0x5ad300['implementation']=function(_0x21ce75,_0x4abb8f,_0x1b5429,_0xdeaadd){if('qsUyU'===_0x487b('0x1c','QYQ)')){if(TrustManagerImplCheckServerTrusted_7015){TrustManagerImplCheckServerTrusted_7015[_0x487b('0x28','WlH7')]=function(_0x4d7efd,_0x5f56fe,_0x165916){console['log'](_0x487b('0x32','j4KY'));return newArrayList();};}}else{console['log']('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(\x27javax.net.ssl.X509TrustManager\x27,\x20\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27com.android.org.conscrypt.OpenSSLEngineImpl\x27)\x20was\x20hooked!');}};}}}var _0x4f6493=undefined;try{_0x4f6493=_0x9cef09['checkServerTrusted']['overload'](_0x487b('0x110','IAeX'),'[Ljava.security.cert.X509Certificate;','java.lang.String',_0x487b('0x55','a@X3'));}catch(_0x2b00de){}finally{if('WXJDp'==='WXJDp'){if(_0x4f6493){if(_0x487b('0x36','bVr3')!==_0x487b('0xd','Ob3d')){var _0x5cc7ae=Java['use']('com.squareup.okhttp.OkHttpClient');_0x5cc7ae[_0x487b('0x2f','EzHt')][_0x487b('0x33','ApF6')]=function(_0x5657db){console[_0x487b('0xa8','q0$6')](_0x487b('0x80','QYQ)'));return this;};}else{_0x4f6493['implementation']=function(_0x5827ca,_0x4736fc,_0x14382c,_0x1dfa9d){if(_0x487b('0xb0','IAeX')===_0x487b('0x4a','ApF6')){TrustManagerImplCheckServerTrusted_8813=TrustManagerImplClz['checkServerTrusted'][_0x487b('0x39','OYI#')](_0x487b('0xaf','RSxm'),'java.lang.String',_0x487b('0x134','^qxC'));}else{console['log']('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(\x27javax.net.ssl.X509TrustManager\x27,\x20\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27com.android.org.conscrypt.OpenSSLSocketImpl\x27)\x20was\x20hooked!');}};}}}else{setTimeout(oooooooooxxxxxxxxx,0x0);}}var _0x5c5d16=undefined;try{_0x5c5d16=_0x9cef09['checkServerTrusted'][_0x487b('0x3','%XuC')]('javax.net.ssl.X509TrustManager',_0x487b('0xbd','MMy8'),'java.lang.String','com.android.org.conscrypt.AbstractConscryptSocket');}catch(_0x19aa7d){}finally{if(_0x5c5d16){if(_0x487b('0xba','!S%l')==='nOsph'){var _0x554f30=okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one[_0x487b('0x83','77jp')](this,Java['use']('gz.justtrustme.Helper')[_0x487b('0x176','EGxG')]());return _0x554f30;}else{_0x5c5d16[_0x487b('0x179','a@X3')]=function(_0x5d6053,_0x3ccee8,_0x47a191,_0x111e42){console['log']('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');};}}}var _0x5d876e=undefined;try{if('KvOfM'==='NFxFj'){TrustManagerImplClz['checkTrustedRecursive']['implementation']=function(_0x4c594d,_0x51a1fe,_0x2ab94d,_0x4646b3,_0x3cc95b,_0x354c1d){var _0x3fa23f=array_list[_0x487b('0xbe','SQ9u')]();return _0x3fa23f;};}else{_0x5d876e=_0x9cef09[_0x487b('0x93','5aGD')]['overload']('javax.net.ssl.X509TrustManager',_0x487b('0x13','u)JU'),'java.lang.String',_0x487b('0x2b','6xh1'));}}catch(_0x1b6f1f){}finally{if(_0x5d876e){if(_0x487b('0x140','%XuC')!=='kUtCR'){console['log'](_0x487b('0x7b','6xh1'));var _0x1c2db3=Java['use']('gz.justtrustme.ImSureItsLegitHostnameVerifier');RequestParamsClassSetHostnameVerifier['call'](this,_0x1c2db3[_0x487b('0x126','qIAu')]());}else{_0x5d876e[_0x487b('0x70','!S%l')]=function(_0x98ff07,_0x66035d,_0x5c73c9,_0x3c3e9b){if('pPxDK'===_0x487b('0x141','6xh1')){console['log']('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.ConscryptEngine)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');}else{if(_0x5c5d16){_0x5c5d16['implementation']=function(_0x216e5d,_0x144d8c,_0x528499,_0x3be7c9){console['log']('static\x20void\x20com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');};}}};}}}}function boringsslhook(){if(!classExists(_0x487b('0xab','wl9z'))){if(_0x487b('0xcd','x1X1')!=='Urfai'){return;}else{var _0x1456ef=Java['use'](_0x487b('0x66','dF#5'));_0x1456ef['enablePublicKeyPinningBypassForLocalTrustAnchors']['implementation']=function(_0x19c096){console[_0x487b('0xf9','44m2')](_0x487b('0x85','44m2'));return _0x1456ef['enablePublicKeyPinningBypassForLocalTrustAnchors'][_0x487b('0xaa','44m2')](this,!![]);};_0x1456ef[_0x487b('0x73','WlH7')][_0x487b('0xf0','EGxG')]=function(_0x4a11cc,_0x13404e,_0x3ba388,_0x1eb0d5){console['log']('org.chromium.net.CronetEngine$Builder\x20addPublicKeyPins\x20was\x20hooked,\x20hostName\x20=\x20'+_0x4a11cc);return this;};}}var _0x1bcee0=Java[_0x487b('0x109','yDxj')](_0x487b('0x10a','dF#5'));var _0x2c63fc=_0x1bcee0['checkServerTrusted']['overload']();_0x2c63fc['implementation']=function(){};}function oooooooooxxxxxxxxx(){Java['perform'](function(){if(_0x487b('0xd3','c5B9')!=='AIxSz'){com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651=com_android_org_conscrypt_Platform_clz[_0x487b('0x77','v^R2')][_0x487b('0x3','%XuC')]('javax.net.ssl.X509TrustManager','[Ljava.security.cert.X509Certificate;','java.lang.String','com.android.org.conscrypt.AbstractConscryptSocket');}else{var _0xfe4f7b=Java['use'](_0x487b('0x8a','KYvS'));var _0x10dc0c=Java[_0x487b('0x6','dF#5')]('org.apache.http.impl.client.DefaultHttpClient');var _0x12e1b4=_0x10dc0c['$init'][_0x487b('0x166','u)JU')]('org.apache.http.conn.ClientConnectionManager','org.apache.http.params.HttpParams');_0x12e1b4['implementation']=function(_0x1aa90e,_0x38199e){if(_0x487b('0x17b','%XuC')==='bhxJi'){console['log']('okhttp3.internal.tls.OkHostnameVerifier.verify(\x27java.lang.String\x27,\x20\x27javax.net.ssl.SSLSession\x27)\x20was\x20hooked!');return!![];}else{var _0xa9f189=_0x12e1b4['call'](this,_0xfe4f7b[_0x487b('0x49','KYvS')](_0x1aa90e,_0x38199e),_0x38199e);console[_0x487b('0x3d','OYI#')](_0x487b('0x5a','qIAu'));return _0xa9f189;}};var _0x36397a=_0x10dc0c[_0x487b('0x16c','4OZK')]['overload']();_0x36397a[_0x487b('0x179','a@X3')]=function(){if('chOrW'===_0x487b('0xc4','c5B9')){var _0x2edf6f=_0x12e1b4['call'](this,_0xfe4f7b[_0x487b('0x8f','%XuC')](),null);console[_0x487b('0xe3','WlH7')](_0x487b('0x139','l181'));return _0x2edf6f;}else{console['log']('private\x20java.util.List\x20com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean)\x20throws\x20java.security.cert.CertificateException\x20was\x20hooked!');return newArrayList();}};var _0x34ab9b=_0x10dc0c[_0x487b('0xd2','&A)n')][_0x487b('0x43','ApF6')](_0x487b('0xcf','IAeX'));_0x34ab9b['implementation']=function(_0x1b67f7){var _0x3d227e=_0x12e1b4['call'](this,_0xfe4f7b[_0x487b('0x171','QYQ)')](),_0x1b67f7);console[_0x487b('0x81','R%CU')](_0x487b('0x172','^HMm'));return _0x3d227e;};var _0x38f7ff=0x3;var _0x57ce54=Java['use'](_0x487b('0xf3','^HMm'));var _0x238160=_0x57ce54[_0x487b('0xed','qIAu')][_0x487b('0x79','x$&t')]('[Ljava.security.cert.X509Certificate;',_0x487b('0xd9','Ewrm'),_0x487b('0xe6','!S%l'));_0x238160['implementation']=function(_0x165b3f,_0x522062,_0x5d6ecf){if(_0x487b('0x123','j4KY')!==_0x487b('0x8c','%XuC')){Java['perform'](function(){Java[_0x487b('0x130','w5gP')](dexfile)['load']();});}else{console['log']('android.net.http.X509TrustManagerExtensions.checkServerTrusted(\x27[Ljava.security.cert.X509Certificate;\x27,\x20\x27java.lang.String\x27,\x20\x27java.lang.String\x27)\x20was\x20hooked!');return Java[_0x487b('0x7e','7MlP')]('java.util.Arrays$ArrayList')['$new'](_0x165b3f);}};var _0x47a306=Java['use'](_0x487b('0xdd','R%CU'));var _0x53ef29=_0x47a306['checkPins'][_0x487b('0x15a','&A)n')]('java.util.List');_0x53ef29['implementation']=function(_0x1c6230){if(_0x487b('0x174','TUxD')===_0x487b('0xc2','5aGD')){console['log'](_0x487b('0x12b','5aGD'));}else{console['log'](_0x487b('0x17e','MMy8'));sslErrorHandler['proceed']();return;}};var _0xbebbc1=Java['use'](_0x487b('0x183','TIBI'));var _0x2f8fd7=_0xbebbc1[_0x487b('0xd2','&A)n')]['overload']('java.lang.String','java.security.KeyStore','java.lang.String',_0x487b('0x137','MMy8'),'java.security.SecureRandom','org.apache.http.conn.scheme.HostNameResolver');_0x2f8fd7[_0x487b('0x5f','dF#5')]=function(_0xc06abb,_0x2a818e,_0x9f3d86,_0x18936c,_0x33bbbe,_0x3aa4e8){var _0x2f5065=_0x2f8fd7[_0x487b('0xb8','v^R2')](this,_0xc06abb,_0x2a818e,_0x9f3d86,_0x18936c,_0x33bbbe,_0x3aa4e8);console[_0x487b('0xb3','wl9z')](_0x487b('0x4f','IAeX'));if(_0xfe4f7b['reInitSSLSocketFactory'](this,_0xc06abb,_0x2a818e,_0x9f3d86,_0x18936c,_0x33bbbe,_0x3aa4e8)){if(_0x487b('0x94','T]f3')!==_0x487b('0x9e','x$&t')){console['log']('替换trustmanagers参数成功!');}else{console[_0x487b('0xc0','j4KY')](_0x487b('0x42','77jp'));}}else{console[_0x487b('0x74','w5gP')](_0x487b('0x17f','77jp'));}return _0x2f5065;};var _0x24254=_0xbebbc1[_0x487b('0x62','R%CU')][_0x487b('0x17d','WlH7')]();var _0x15adb1=_0xbebbc1[_0x487b('0x12','5aGD')]['overload']();_0x24254[_0x487b('0xec',')XWS')]=function(){console[_0x487b('0x3c','TUxD')](_0x487b('0xc8','fpwA'));return _0xbebbc1[_0x487b('0x13f','5aGD')]();};var _0x20ad71=_0xbebbc1[_0x487b('0xbc','SQ9u')]['overload']('java.net.Socket');_0x20ad71[_0x487b('0x13c','TUxD')]=function(_0x1e1b1c){if(_0x487b('0x89','kRt3')===_0x487b('0x16b','yDxj')){console[_0x487b('0xa8','q0$6')]('android.security.net.config.NetworkSecurityTrustManager.checkPins(\x27java.util.List\x27)\x20was\x20hooked!');}else{console[_0x487b('0xe3','WlH7')]('org.apache.http.conn.ssl.SSLSocketFactory.isSecure(\x27java.net.Socket\x27)\x20was\x20hooked!');return!![];}};var _0x4297ea=Java[_0x487b('0x51','v^R2')](_0x487b('0x1a','x1X1'));var _0x226d96=_0x4297ea[_0x487b('0x5d','7MlP')]['overload']();_0x226d96['implementation']=function(){var _0x38f5fd=_0x226d96['call'](this);console['log'](_0x487b('0x15d','fpwA'));return _0xfe4f7b['replaceGetTrustManagers'](this,_0x38f5fd);};var _0x57abb3=Java['use'](_0x487b('0x175','v^R2'));var _0x2cf171=_0x57abb3[_0x487b('0x4b','Ob3d')]['overload'](_0x487b('0x10','ApF6'));_0x2cf171['implementation']=function(_0x155e79){console['log'](_0x487b('0x162','x$&t'));};var _0x53216d=_0x57abb3[_0x487b('0xa','IutC')]['overload'](_0x487b('0x15b','T]f3'));_0x53216d['implementation']=function(_0x13c153){console['log'](_0x487b('0x168','6xh1'));};var _0x54f808=_0x57abb3[_0x487b('0x23','44m2')][_0x487b('0x25','6xh1')]('javax.net.ssl.SSLSocketFactory');_0x54f808[_0x487b('0x1','l181')]=function(_0x34f537){if('GvjZu'!==_0x487b('0xe7','a@X3')){console['log'](_0x487b('0x9a','WlH7'));return;}else{console[_0x487b('0x10c','IAeX')](_0x487b('0xc3','w5gP'));}};var _0x2990d5=Java[_0x487b('0x104','%XuC')](_0x487b('0x19','&A)n'));var _0x1df7e4=Java['registerClass']({'implements':[_0x2990d5],'methods':{'checkClientTrusted'(_0x244dbe,_0x26ed11){},'checkServerTrusted'(_0x457d68,_0x528e41){},'getAcceptedIssuers'(){if(_0x487b('0x14','5aGD')!==_0x487b('0xb6','c5B9')){return[];}else{var _0x437bdb=Java[_0x487b('0x13d','Ob3d')]('okhttp3.CertificatePinner');var _0xa516c7=_0x437bdb[_0x487b('0x12e','dF#5')]['overload'](_0x487b('0x31','RSxm'),_0x487b('0xf8','a@X3'));_0xa516c7['implementation']=function(_0x12f8a6,_0x14172a){console[_0x487b('0xd6','EzHt')]('okhttp3.CertificatePinner.check(\x27java.lang.String\x27,\x20\x27java.util.List\x27)\x20was\x20hooked!');};}}},'name':_0x487b('0x151','Ob3d')});var _0x5bcc89=[_0x1df7e4['$new']()];var _0x3e8587=Java['use']('javax.net.ssl.SSLContext');var _0xe8ba6=_0x3e8587['init'][_0x487b('0xf6','&kEb')]('[Ljavax.net.ssl.KeyManager;','[Ljavax.net.ssl.TrustManager;',_0x487b('0x95','kRt3'));_0xe8ba6[_0x487b('0xbb','x$&t')]=function(_0x282d1f,_0xcd29ba,_0x35d8af){_0xe8ba6[_0x487b('0x15','x$&t')](this,_0x282d1f,_0x5bcc89,_0x35d8af);console[_0x487b('0xb','v^R2')]('javax.net.ssl.SSLContext.init(\x27[Ljavax.net.ssl.KeyManager;\x27,\x20\x27[Ljavax.net.ssl.TrustManager;\x27,\x20\x27java.security.SecureRandom\x27)\x20was\x20hooked!');};var _0x5d0835=Java[_0x487b('0x59','OYI#')](_0x487b('0x107','u)JU'));var _0x46fcea=_0x5d0835[_0x487b('0x6b','w5gP')][_0x487b('0x186','!S%l')](_0x487b('0xdf','yDxj'));_0x46fcea['implementation']=function(_0x4581cb){if(_0x487b('0x16a','7MlP')==='rCnnW'){var _0x5a8e3c=_0x1e249b[_0x487b('0xf2','&kEb')]();return _0x5a8e3c;}else{_0x46fcea[_0x487b('0x11d','EzHt')](this,_0x4581cb);var _0x364542=_0x4581cb['getClassLoader']();}};if(hasTrustManagerImpl()){var _0x1c5415=Java[_0x487b('0x21','KYvS')](_0x487b('0xad','77jp'));var _0x510ef9=undefined;try{if(_0x487b('0x26','^HMm')==='UvtoM'){_0x510ef9=_0x1c5415['checkServerTrusted'][_0x487b('0x4d','MMy8')]('[Ljava.security.cert.X509Certificate;','java.lang.String',_0x487b('0xe9','x1X1'));}else{com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474[_0x487b('0x16f','v^R2')]=function(_0xd62904,_0x59306e,_0x5de169,_0x4102c9){console[_0x487b('0x182','5aGD')](_0x487b('0x13a','x$&t'));};}}catch(_0x1c7533){}finally{if(_0x487b('0x187','qIAu')==='WSTdG'){if(_0x510ef9){_0x510ef9[_0x487b('0x16f','v^R2')]=function(_0x55e426,_0x2a4e93,_0xe9063c){if('CVGjE'===_0x487b('0x8b','TIBI')){console[_0x487b('0xfa','Ewrm')](_0x487b('0x20','Ob3d'));return newArrayList();}else{if(com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928){com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928['implementation']=function(_0x2bc8bd,_0x5cd4a7,_0x122029,_0x2e3160){console[_0x487b('0xe3','WlH7')](_0x487b('0x177','fpwA'));};}}};}}else{var _0x523418=Java[_0x487b('0xd8','MMy8')](_0x487b('0x12c','^qxC'));_0x523418['onReceivedSslError']['implementation']=function(_0xd55a2a,_0x39e42f,_0x1a5868){console[_0x487b('0xde',')XWS')]('WebViewClient\x20onReceivedSslError\x20was\x20hooked!');_0x39e42f[_0x487b('0x11e','6xh1')]();return;};_0x523418['onReceivedError']['overload'](_0x487b('0x2e','44m2'),'int',_0x487b('0x14b','&kEb'),'java.lang.String')[_0x487b('0xe0','Ob3d')]=function(_0x318b89,_0x5cd523,_0x2f0a9e,_0x2e8c4e){console[_0x487b('0x113','IutC')](_0x487b('0x146','qIAu'));return;};_0x523418[_0x487b('0x14d','kRt3')]['overload'](_0x487b('0x145','Ob3d'),'android.webkit.WebResourceRequest',_0x487b('0x34','RSxm'))['implementation']=function(){console[_0x487b('0x37','Ob3d')]('WebViewClient\x20onReceivedError\x20was\x20hooked!');return;};}}var _0x1d91cf=undefined;try{if('GxciJ'!=='esdfx'){_0x1d91cf=_0x1c5415[_0x487b('0x103','44m2')]['overload']('[Ljava.security.cert.X509Certificate;','java.lang.String',_0x487b('0x16','kRt3'));}else{Java['openClassFile'](dexfile)[_0x487b('0xc1','6xh1')]();}}catch(_0x3064d5){}finally{if(_0x487b('0xa9','R%CU')==='OKnEb'){if(_0x1d91cf){_0x1d91cf['implementation']=function(_0x2be686,_0x391c01,_0x1f35ed){console['log'](_0x487b('0x9d','v^R2'));return newArrayList();};}}else{var _0x4716c6=Java[_0x487b('0x46','kRt3')](_0x487b('0x7c','x1X1'));return _0x4716c6['$new']();}}var _0x3c4921=undefined;try{_0x3c4921=_0x1c5415['checkTrusted'][_0x487b('0x132','a@X3')]('[Ljava.security.cert.X509Certificate;',_0x487b('0x170','WlH7'),_0x487b('0x3a','WlH7'),'javax.net.ssl.SSLParameters','boolean');}catch(_0x2f7336){}finally{if(_0x487b('0x45','x$&t')!==_0x487b('0xc9','@vff')){if(_0x3c4921){_0x3c4921['implementation']=function(_0x17cf2c,_0x533000,_0x5f2006,_0x4cecde,_0x2dfc97){if('iBnvi'!=='Zjxpz'){console[_0x487b('0xf1','a@X3')](_0x487b('0xb2','T]f3'));return newArrayList();}else{return null;}};}}else{try{var _0xc986c1=Java['use'](_0x487b('0x2d','x1X1'));var _0x1519f1=_0xc986c1[_0x487b('0x10e','SQ9u')]['overload']('java.lang.String','java.util.List');_0x1519f1[_0x487b('0xeb','qIAu')]=function(_0x57ca35,_0x1e737d){console[_0x487b('0xc6','RSxm')]('okhttp3.CertificatePinner.check(\x27java.lang.String\x27,\x20\x27java.util.List\x27)\x20was\x20hooked!');};}catch(_0x1e8617){}}}var _0x438971=undefined;try{if(_0x487b('0x7f','6xh1')!=='OLrTP'){_0x438971=_0x1c5415[_0x487b('0x64','&kEb')][_0x487b('0x15a','&A)n')](_0x487b('0x111','w5gP'),'[B','[B','java.lang.String',_0x487b('0x12f','EzHt'),'boolean');}else{return;}}catch(_0x2c79ec){}finally{if(_0x438971){_0x438971['implementation']=function(_0x21b8e7,_0xcf806b,_0x339837,_0x1e1c5c,_0x4523ff,_0x3e0c3c){console[_0x487b('0xde',')XWS')](_0x487b('0x9b','EGxG'));return newArrayList();};}}try{var _0x1e249b=Java[_0x487b('0xd4','44m2')]('java.util.ArrayList');if(_0x1c5415[_0x487b('0x114','5aGD')]){if('WDqdK'==='FRKjO'){if(com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474){com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474['implementation']=function(_0x12c94d,_0x876a09,_0x2b34a3,_0x4e9943){console[_0x487b('0x105','!S%l')](_0x487b('0xb1','w5gP'));};}}else{_0x1c5415[_0x487b('0xef','yDxj')]['implementation']=function(_0x102419,_0x34e76f,_0x3a196a,_0x44b509,_0x157525,_0x2ad3cd){if(_0x487b('0x58','bVr3')==='kXEvY'){var _0x45b5e9=_0x1e249b['$new']();return _0x45b5e9;}else{if(classExists('org.xutils.http.RequestParams')){var _0x2b63ae=Java['use']('org.xutils.http.RequestParams');var _0x41d539=_0x2b63ae[_0x487b('0x155','^HMm')]['overload']('javax.net.ssl.SSLSocketFactory');_0x41d539[_0x487b('0xca','%XuC')]=function(_0x6cccd3){console['log']('org.xutils.http.RequestParams.setSslSocketFactory(\x27javax.net.ssl.SSLSocketFactory\x27)\x20was\x20hooked!');var _0x54f0bb=Java['use']('gz.justtrustme.Helper');_0x41d539['call'](this,_0x54f0bb[_0x487b('0x82','5aGD')]());};var _0xc1c84f=_0x2b63ae['setHostnameVerifier'][_0x487b('0xa1','77jp')](_0x487b('0x11c','v^R2'));_0xc1c84f[_0x487b('0x13c','TUxD')]=function(_0x48f0b1){console['log'](_0x487b('0x181','5aGD'));var _0x243587=Java[_0x487b('0x125','T]f3')]('gz.justtrustme.ImSureItsLegitHostnameVerifier');_0xc1c84f['call'](this,_0x243587['$new']());};}}};}}}catch(_0x1c3538){}}b0ringsslp();kaooqpjakk10a();klalo1mmmmal();xmalk11o_JKKJKK();xnalkak11ll0ppi0000000ppp();xxxxkakjakkkk();boringsslhook();}});}function jte(){Java['perform'](function(){var _0x2cbffd=Java[_0x487b('0x13d','Ob3d')](_0x487b('0x115','5aGD'))[_0x487b('0x5c','IutC')]()['getApplicationContext']();var _0x11461c=_0x2cbffd[_0x487b('0x149','EGxG')]();console['log']('Package\x20name:\x20'+_0x11461c);if(_0x11461c['startsWith'](_0x7f80('qkn.gg.olmhkum'))){}else{if(_0x487b('0xee','EzHt')!==_0x487b('0x100','EzHt')){setTimeout(oooooooooxxxxxxxxx,0x0);}else{console[_0x487b('0x8','^qxC')](_0x487b('0x160','j4KY'));}}});}setTimeout(jte,0x7d0); \ No newline at end of file +var loadedXRadar = false; +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); +}; + + +loadXRadarDexfile(); + +function hasTrustManagerImpl() { + return classExists("com.android.org.conscrypt.TrustManagerImpl"); +} + +function newArrayList() { + var ArrayListClz = Java.use('java.util.ArrayList'); + return ArrayListClz.$new(); +} + + +function processOkHttp() { + //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦 + if (classExists("com.squareup.okhttp.CertificatePinner")) { + var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner'); + var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + }else{ + console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算了。不同系统不一样,不用找了!!!"); + } + + if (classExists("okhttp3.CertificatePinner")) { + try { + var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner'); + var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List'); + okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) { + //什么都不做 + console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!"); + }; + } catch (error) { + console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) { + try { + var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier'); + var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return true; + }; + var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate'); + OkHostnameVerifierVerify_8978.implementation = function(v0, v1) { + //强制返回true + console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!"); + return true; + }; + } catch (error) { + console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!"); + } + + if (classExists("okhttp3.OkHttpClient$Builder")) { + try{ + var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder'); + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory()); + return ret; + }; + var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager'); + okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) { + //把参数替换成EmptySSLFactory + var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager); + return ret; + }; + } catch(error) { + console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!"); + } + }else{ + console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!"); + } +} + + +function processXutils() { + if (classExists("org.xutils.http.RequestParams")) { + var RequestParamsClass = Java.use("org.xutils.http.RequestParams"); + var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + RequestParamsClassSetSslSocketFactory.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + var Helper = Java.use("gz.justtrustme.Helper"); + //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory + RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory()); + }; + var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + RequestParamsClassSetHostnameVerifier.implementation = function(v0) { + console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier"); + //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier + RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new()); + }; + } +} + +function processHttpClientAndroidLib() { + if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) { + var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier"); + var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean'); + OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!"); + }; + }else{ + console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!"); + } +} + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processConscryptPlatform() { + if (!classExists("com.android.org.conscrypt.Platform")) { + return; + } + var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform'); + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl') was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) { + //什么都不做 + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl') was hooked!"); + }; + } + } + + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_4651.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.AbstractConscryptSocket) throws java.security.cert.CertificateException was hooked!"); + }; + } + } + + var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = undefined; + try{ + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine'); + }catch(error) { + + }finally{ + if (com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474) { + com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6474.implementation = function(v0, v1, v2, v3) { + console.log("static void com.android.org.conscrypt.Platform.checkServerTrusted(javax.net.ssl.X509TrustManager,java.security.cert.X509Certificate[],java.lang.String,com.android.org.conscrypt.ConscryptEngine) throws java.security.cert.CertificateException was hooked!"); + }; + } + } +} + + +//这是hooker添加的hook点,原JustTrustMe中没有的 +function processPinningTrustManager() { + if (!classExists("appcelerator.https.PinningTrustManager")) { + return; + } + var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager'); + var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload(); + pinningTrustManagerClass_checkServerTrusted.implementation = function() { + //什么都不做 + }; +} + + +Java.perform(function() { + var Helper = Java.use("gz.justtrustme.Helper"); + var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient"); + //被强奸的构造方法 + var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams'); + DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) { + //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload(); + DefaultHttpClientClassInit_1602.implementation = function() { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!"); + return returnObj; + }; + var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams'); + DefaultHttpClientClassInit_1603.implementation = function(v0) { + //使用DefaultHttpClientClassRapeConstructor强奸它 + var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0); + console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!"); + return returnObj; + }; + //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了 + var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions'); + var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) { + console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return Java.use('java.util.Arrays$ArrayList').$new(certsArr); + }; + var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager'); + var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List'); + NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) { + //什么都不做 + console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!"); + }; + + //替换trustmanagers参数 + var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory'); + var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver'); + SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) { + var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5); + console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!"); + if (Helper.reInitSSLSocketFactory(this, v0, v1, v2, v3, v4, v5)) { + console.log("替换trustmanagers参数成功!"); + }else{ + console.log("替换trustmanagers参数失败!"); + } + return returnObj; + }; + + var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload(); + var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload(); + SSLSocketFactoryGetSocketFactoryMethod.implementation = function() { + //强制用空的构造方法 + console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!"); + return SSLSocketFactory.$new(); + }; + + var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket'); + SSLSocketFactoryIsSecure.implementation = function(v0) { + //强制返回true + console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!"); + return true; + }; + + var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory'); + var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload(); + TrustManagerFactoryGetTrustManagers.implementation = function() { + var ret = TrustManagerFactoryGetTrustManagers.call(this); + //替换getTrustManagers方法逻辑 + console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!"); + return Helper.replaceGetTrustManagers(this, ret); + }; + + var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); + var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier'); + HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!"); + }; + + var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory'); + HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) { + //什么都不做 + console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!"); + }; + + var SSLContextClz = Java.use('javax.net.ssl.SSLContext'); + var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom'); + SSLContextClzInit.implementation = function(v0, v1, v2) { + //将第二个参数强制替换为我们自己构造的不安全的TrustManagers + SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2); + console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!"); + }; + + var ApplicationClz = Java.use('android.app.Application'); + var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context'); + ApplicationClzAttach.implementation = function(context) { + ApplicationClzAttach.call(this, context); + //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码 + var classLoader = context.getClassLoader(); + + }; + + if (hasTrustManagerImpl()) { + var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl'); + var TrustManagerImplCheckServerTrusted_8813 = undefined; + try{ + TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckServerTrusted_8813) { + TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckServerTrusted_7015 = undefined; + try{ + TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally { + if (TrustManagerImplCheckServerTrusted_7015) { + TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_5587 = undefined; + try{ + TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_5587) { + TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_9999 = undefined; + try{ + TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean'); + + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_9999) { + TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) { + console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!"); + return newArrayList(); + }; + } + } + var TrustManagerImplCheckTrusted_2872 = undefined; + try{ + TrustManagerImplCheckTrusted_2872 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', '[B', '[B', 'java.lang.String', 'java.lang.String', 'boolean'); + }catch(error){ + //console.error(error + " 不要在意这个错误!"); + }finally{ + if (TrustManagerImplCheckTrusted_2872) { + TrustManagerImplCheckTrusted_2872.implementation = function(v0, v1, v2, v3, v4, v5) { + console.log('private java.util.List com.android.org.conscrypt.TrustManagerImpl.checkTrusted(java.security.cert.X509Certificate[],byte[],byte[],java.lang.String,java.lang.String,boolean) throws java.security.cert.CertificateException was hooked!'); + return newArrayList(); + }; + } + } + } + + processOkHttp(); + processXutils(); + processHttpClientAndroidLib(); + //hooker添加的hook点 + processConscryptPlatform(); + processPinningTrustManager(); +}) \ No newline at end of file diff --git a/js/just_trust_me_for_ios.js b/js/just_trust_me_for_ios.js deleted file mode 100644 index 068fd0e..0000000 --- a/js/just_trust_me_for_ios.js +++ /dev/null @@ -1,79 +0,0 @@ -var SecTrustEvaluate_handle = - Module.findExportByName('Security', 'SecTrustEvaluate'); -var SecTrustEvaluateWithError_handle = - Module.findExportByName('Security', 'SecTrustEvaluateWithError'); -var SSL_CTX_set_custom_verify_handle = - Module.findExportByName('libboringssl.dylib', 'SSL_CTX_set_custom_verify'); -var SSL_get_psk_identity_handle = - Module.findExportByName('libboringssl.dylib', 'SSL_get_psk_identity'); -var boringssl_context_set_verify_mode_handle = Module.findExportByName( - 'libboringssl.dylib', 'boringssl_context_set_verify_mode'); - -if (SecTrustEvaluateWithError_handle) { - var SecTrustEvaluateWithError = new NativeFunction( - SecTrustEvaluateWithError_handle, 'int', ['pointer', 'pointer']); - - Interceptor.replace( - SecTrustEvaluateWithError_handle, - new NativeCallback(function(trust, error) { - console.log('[*] Called SecTrustEvaluateWithError()'); - SecTrustEvaluateWithError(trust, NULL); - Memory.writeU8(error, 0); - return 1; - }, 'int', ['pointer', 'pointer'])); - console.log('[+] SecTrustEvaluateWithError() hook installed.'); -} - -if (SecTrustEvaluate_handle) { - var SecTrustEvaluate = new NativeFunction( - SecTrustEvaluate_handle, 'int', ['pointer', 'pointer']); - - Interceptor.replace( - SecTrustEvaluate_handle, new NativeCallback(function(trust, result) { - console.log('[*] Called SecTrustEvaluate()'); - SecTrustEvaluate(trust, result); - Memory.writeU8(result, 1); - return 0; - }, 'int', ['pointer', 'pointer'])); - console.log('[+] SecTrustEvaluate() hook installed.'); -} - -if (SSL_CTX_set_custom_verify_handle) { - var SSL_CTX_set_custom_verify = new NativeFunction( - SSL_CTX_set_custom_verify_handle, 'void', ['pointer', 'int', 'pointer']); - - var replaced_callback = new NativeCallback(function(ssl, out) { - console.log('[*] Called custom SSL verifier') - return 0; - }, 'int', ['pointer', 'pointer']); - - Interceptor.replace( - SSL_CTX_set_custom_verify_handle, - new NativeCallback(function(ctx, mode, callback) { - console.log('[*] Called SSL_CTX_set_custom_verify()'); - SSL_CTX_set_custom_verify(ctx, 0, replaced_callback); - }, 'int', ['pointer', 'int', 'pointer'])); - console.log('[+] SSL_CTX_set_custom_verify() hook installed.') -} - -if (SSL_get_psk_identity_handle) { - Interceptor.replace( - SSL_get_psk_identity_handle, new NativeCallback(function(ssl) { - console.log('[*] Called SSL_get_psk_identity_handle()'); - return 'notarealPSKidentity'; - }, 'pointer', ['pointer'])); - console.log('[+] SSL_get_psk_identity() hook installed.') -} - -if (boringssl_context_set_verify_mode_handle) { - var boringssl_context_set_verify_mode = new NativeFunction( - boringssl_context_set_verify_mode_handle, 'int', ['pointer', 'pointer']); - - Interceptor.replace( - boringssl_context_set_verify_mode_handle, - new NativeCallback(function(a, b) { - console.log('[*] Called boringssl_context_set_verify_mode()'); - return 0; - }, 'int', ['pointer', 'pointer'])); - console.log('[+] boringssl_context_set_verify_mode() hook installed.') -} \ No newline at end of file diff --git a/js/just_trust_me_okhttp_hook_finder.js b/js/just_trust_me_okhttp_hook_finder.js new file mode 100644 index 0000000..d1aaab9 --- /dev/null +++ b/js/just_trust_me_okhttp_hook_finder.js @@ -0,0 +1,72 @@ +function loadDexfile(dexfile) { + Java.perform(function () { + Java.openClassFile(dexfile).load(); + }); +}; + + +loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); + + +function printOkhttp3FakeClass(okhttp3FakeClassObject) { + if (okhttp3FakeClassObject) { + console.log("-----------------------------------------------------------------------"); + console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); + console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); + console.log("\n"); + for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { + let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); + console.log("混淆方法" + k + ":"); + console.log("原方法签名:" + fakeMethod.getOriginalMethod()); + console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); + console.log("\n"); + } + if (okhttp3FakeClassObject.fakeMethodSize() == 0) { + //手动找混淆方法 + console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); + } + } +} + +//设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 +//一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 +//如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 +//你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! +var matchClassNameRegExp = RegExp(/^okhttp3/); + + +Java.perform(function() { + var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.match(matchClassNameRegExp)) { + try { + let tempClassHandle = Java.use(className).class; + let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); + if (matchOkHttpClientBuilderCheckResult) { + printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); + return; + } + + let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); + if (matchOk3CertificatePinnerCheckResult) { + printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); + return; + } + + let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); + if (matchOk3OkHostnameVerifierVerifyResult) { + printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); + return; + } + + } catch(error) { + //console.error(error); + } + } + }, + onComplete: function() {} + }); +}); + + diff --git a/js/object_store.js b/js/object_store.js index 1de0737..b496af8 100644 --- a/js/object_store.js +++ b/js/object_store.js @@ -4,7 +4,7 @@ function loadDexfile(dexfile) { }); }; -loadDexfile('/data/local/tmp/radar.dex'); +loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); function log(str) { diff --git a/js/r0capture.js b/js/r0capture.js deleted file mode 100644 index f1c4acb..0000000 --- a/js/r0capture.js +++ /dev/null @@ -1,349 +0,0 @@ -/** - * Initializes 'addresses' dictionary and NativeFunctions. - */ -"use strict"; -rpc.exports = { - setssllib: function (name) { - console.log("setSSLLib => " + name); - libname = name; - initializeGlobals(); - return; - } -}; - -var addresses = {}; -var SSL_get_fd = null; -var SSL_get_session = null; -var SSL_SESSION_get_id = null; -var getpeername = null; -var getsockname = null; -var ntohs = null; -var ntohl = null; -var SSLstackwrite = null; -var SSLstackread = null; - -var libname = "*libssl*"; - -function uuid(len, radix) { - var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); - var uuid = [], i; - radix = radix || chars.length; - - if (len) { - // Compact form - for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; - } else { - // rfc4122, version 4 form - var r; - - // rfc4122 requires these characters - uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; - uuid[14] = '4'; - - // Fill in random data. At i==19 set the high bits of clock sequence as - // per rfc4122, sec. 4.1.5 - for (i = 0; i < 36; i++) { - if (!uuid[i]) { - r = 0 | Math.random() * 16; - uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; - } - } - } - - return uuid.join(''); -} -function return_zero(args) { - return 0; -} -function initializeGlobals() { - var resolver = new ApiResolver("module"); - var exps = [ - [Process.platform == "darwin" ? "*libboringssl*" : "*libssl*", ["SSL_read", "SSL_write", "SSL_get_fd", "SSL_get_session", "SSL_SESSION_get_id"]], // for ios and Android - [Process.platform == "darwin" ? "*libsystem*" : "*libc*", ["getpeername", "getsockname", "ntohs", "ntohl"]] - ]; - // console.log(exps) - for (var i = 0; i < exps.length; i++) { - var lib = exps[i][0]; - var names = exps[i][1]; - for (var j = 0; j < names.length; j++) { - var name = names[j]; - // console.log("exports:" + lib + "!" + name) - var matches = resolver.enumerateMatchesSync("exports:" + lib + "!" + name); - if (matches.length == 0) { - if (name == "SSL_get_fd") { - addresses["SSL_get_fd"] = 0; - continue; - } - throw "Could not find " + lib + "!" + name; - } - else if (matches.length != 1) { - // Sometimes Frida returns duplicates. - var address = 0; - var s = ""; - var duplicates_only = true; - for (var k = 0; k < matches.length; k++) { - if (s.length != 0) { - s += ", "; - } - s += matches[k].name + "@" + matches[k].address; - if (address == 0) { - address = matches[k].address; - } - else if (!address.equals(matches[k].address)) { - duplicates_only = false; - } - } - if (!duplicates_only) { - throw "More than one match found for " + lib + "!" + name + ": " + s; - } - } - addresses[name] = matches[0].address; - } - } - if (addresses["SSL_get_fd"] == 0) { - SSL_get_fd = return_zero; - } else { - SSL_get_fd = new NativeFunction(addresses["SSL_get_fd"], "int", ["pointer"]); - } - SSL_get_session = new NativeFunction(addresses["SSL_get_session"], "pointer", ["pointer"]); - SSL_SESSION_get_id = new NativeFunction(addresses["SSL_SESSION_get_id"], "pointer", ["pointer", "pointer"]); - getpeername = new NativeFunction(addresses["getpeername"], "int", ["int", "pointer", "pointer"]); - getsockname = new NativeFunction(addresses["getsockname"], "int", ["int", "pointer", "pointer"]); - ntohs = new NativeFunction(addresses["ntohs"], "uint16", ["uint16"]); - ntohl = new NativeFunction(addresses["ntohl"], "uint32", ["uint32"]); -} -initializeGlobals(); - -function ipToNumber(ip) { - var num = 0; - if (ip == "") { - return num; - } - var aNum = ip.split("."); - if (aNum.length != 4) { - return num; - } - num += parseInt(aNum[0]) << 0; - num += parseInt(aNum[1]) << 8; - num += parseInt(aNum[2]) << 16; - num += parseInt(aNum[3]) << 24; - num = num >>> 0;//这个很关键,不然可能会出现负数的情况 - return num; -} - -/** - * Returns a dictionary of a sockfd's "src_addr", "src_port", "dst_addr", and - * "dst_port". - * @param {int} sockfd The file descriptor of the socket to inspect. - * @param {boolean} isRead If true, the context is an SSL_read call. If - * false, the context is an SSL_write call. - * @return {dict} Dictionary of sockfd's "src_addr", "src_port", "dst_addr", - * and "dst_port". - */ -function getPortsAndAddresses(sockfd, isRead) { - var message = {}; - var src_dst = ["src", "dst"]; - for (var i = 0; i < src_dst.length; i++) { - if ((src_dst[i] == "src") ^ isRead) { - var sockAddr = Socket.localAddress(sockfd) - } - else { - var sockAddr = Socket.peerAddress(sockfd) - } - if (sockAddr == null) { - // 网络超时or其他原因可能导致socket被关闭 - message[src_dst[i] + "_port"] = 0 - message[src_dst[i] + "_addr"] = 0 - } else { - message[src_dst[i] + "_port"] = (sockAddr.port & 0xFFFF) - message[src_dst[i] + "_addr"] = ntohl(ipToNumber(sockAddr.ip.split(":").pop())) - } - } - return message; -} -/** - * Get the session_id of SSL object and return it as a hex string. - * @param {!NativePointer} ssl A pointer to an SSL object. - * @return {dict} A string representing the session_id of the SSL object's - * SSL_SESSION. For example, - * "59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336". - */ -function getSslSessionId(ssl) { - var session = SSL_get_session(ssl); - if (session == 0) { - return 0; - } - var len = Memory.alloc(4); - var p = SSL_SESSION_get_id(session, len); - len = Memory.readU32(len); - var session_id = ""; - for (var i = 0; i < len; i++) { - // Read a byte, convert it to a hex string (0xAB ==> "AB"), and append - // it to session_id. - session_id += - ("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2); - } - return session_id; -} - -Interceptor.attach(addresses["SSL_read"], - { - onEnter: function (args) { - var message = getPortsAndAddresses(SSL_get_fd(args[0]), true); - message["ssl_session_id"] = getSslSessionId(args[0]); - message["function"] = "SSL_read"; - message["stack"] = SSLstackread; - this.message = message; - this.buf = args[1]; - }, - onLeave: function (retval) { - retval |= 0; // Cast retval to 32-bit integer. - if (retval <= 0) { - return; - } - send(this.message, Memory.readByteArray(this.buf, retval)); - } - }); - -Interceptor.attach(addresses["SSL_write"], - { - onEnter: function (args) { - var message = getPortsAndAddresses(SSL_get_fd(args[0]), false); - message["ssl_session_id"] = getSslSessionId(args[0]); - message["function"] = "SSL_write"; - message["stack"] = SSLstackwrite; - send(message, Memory.readByteArray(args[1], parseInt(args[2]))); - }, - onLeave: function (retval) { - } - }); - -if (Java.available) { - Java.perform(function () { - function storeP12(pri, p7, p12Path, p12Password) { - var X509Certificate = Java.use("java.security.cert.X509Certificate") - var p7X509 = Java.cast(p7, X509Certificate); - var chain = Java.array("java.security.cert.X509Certificate", [p7X509]) - var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); - ks.load(null, null); - ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain); - try { - var out = Java.use("java.io.FileOutputStream").$new(p12Path); - ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray()) - } catch (exp) { - console.log(exp) - } - } - //在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue - Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () { - var result = this.getPrivateKey() - var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); - storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); - var message = {}; - message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; - message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); - var data = Memory.alloc(1); - send(message, Memory.readByteArray(data, 1)) - return result; - } - Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () { - var result = this.getCertificateChain() - var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); - storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); - var message = {}; - message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; - message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); - var data = Memory.alloc(1); - send(message, Memory.readByteArray(data, 1)) - return result; - } - - //SSLpinning helper 帮助定位证书绑定的关键代码a - Java.use("java.io.File").$init.overload('java.io.File', 'java.lang.String').implementation = function (file, cert) { - var result = this.$init(file, cert) - var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); - if (file.getPath().indexOf("cacert") >= 0 && stack.indexOf("X509TrustManagerExtensions.checkServerTrusted") >= 0) { - var message = {}; - message["function"] = "SSLpinning position locator => " + file.getPath() + " " + cert; - message["stack"] = stack; - var data = Memory.alloc(1); - send(message, Memory.readByteArray(data, 1)) - } - return result; - } - - - Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) { - var result = this.socketWrite0(fd, bytearry, offset, byteCount); - var message = {}; - message["function"] = "HTTP_send"; - message["ssl_session_id"] = ""; - message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); - message["src_port"] = parseInt(this.socket.value.getLocalPort().toString()); - message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); - message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); - message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - var ptr = Memory.alloc(byteCount); - for (var i = 0; i < byteCount; ++i) - Memory.writeS8(ptr.add(i), bytearry[offset + i]); - send(message, Memory.readByteArray(ptr, byteCount)) - return result; - } - Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) { - var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout); - var message = {}; - message["function"] = "HTTP_recv"; - message["ssl_session_id"] = ""; - message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); - message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); - message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); - message["dst_port"] = parseInt(this.socket.value.getLocalPort()); - message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - if (result > 0) { - var ptr = Memory.alloc(result); - for (var i = 0; i < result; ++i) - Memory.writeS8(ptr.add(i), bytearry[offset + i]); - send(message, Memory.readByteArray(ptr, result)) - } - return result; - } - - if (parseFloat(Java.androidVersion) > 8) { - Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { - var result = this.write(bytearry, int1, int2); - SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - return result; - } - Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { - var result = this.read(bytearry, int1, int2); - SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - return result; - } - } - else { - Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { - var result = this.write(bytearry, int1, int2); - SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - return result; - } - Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { - var result = this.read(bytearry, int1, int2); - SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); - return result; - } - - } - } - - ) -} - -rpc.exports = { - cleanup: function () { - // 清理所有拦截器 - Interceptor.detachAll(); - // 如果你设置了定时器或 Stalker,也可以清理 - // Stalker.unfollow(); - // clearInterval(...); - } -}; \ No newline at end of file diff --git a/js/radar.js b/js/radar.js new file mode 100644 index 0000000..e98d140 --- /dev/null +++ b/js/radar.js @@ -0,0 +1,218 @@ +function discoverClass(className) { + if (!className) { + return; + } + var radarClz = Java.use("gz.radar.ClassRadar"); + var radarClassResult = radarClz.discoverClass(className); + return radarClassResult; +}; + +//查找所有加载的子类 +function findOffspringsClasses(parentClassName) { + var radarResults = new Array(); + if (!classExists(parentClassName)) { + return radarResults; + } + var radarClz = Java.use("gz.radar.ClassRadar"); + var index = 0; + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (className.startsWith('[') || className.startsWith('org') || className.startsWith('sun') || className.startsWith('com.android') || className.startsWith('java') || className.startsWith('android') || className.startsWith('dalvik') || className.startsWith('com.google') || className.startsWith('libcore') || className.startsWith('gz.')) { + return; + } + try { + var isSubClass = radarClz.isSubClass(className, parentClassName); + if (isSubClass) { + var resultItem = radarClz.discoverClass(className); + if (resultItem) { + radarResults[index] = resultItem; + index++; + } + } + } catch(err) { + //console.error(err); + } + + }, + onComplete: function() {} + }); + return radarResults; +}; + +function findSOLibrary(libname) { + var report = ""; + Java.perform(function() { + var exports = Module.enumerateExportsSync(libname); + exports.map(function(ex) { + report += (ex.address + ": " + ex.name) + "\n"; + }); + report += (libname + " exports num:" + exports.length) + "\n"; + }); + return report; +}; + +//根据类名或正则表达式查找 +function findClasses(classNameRegex) { + var radarResults = new Array(); + if (classExists(classNameRegex)) { + var resultItem = discoverClass(classNameRegex); + if (resultItem) { + radarResults[0] = resultItem; + } + } else { + var clzReg = eval("/" + classNameRegex + "/"); + var index = 0; + Java.enumerateLoadedClasses({ + onMatch: function(className) { + if (clzReg.test(className)) { + var resultItem = discoverClass(className); + if (resultItem) { + radarResults[index] = resultItem; + index++; + } + } + }, + onComplete: function() {} + }); + } + return radarResults; +} + +function generateFridaMethodOverload(clzVarName, radarMethod) { + var overloadJs = ""; + if (!radarMethod.isLocal.value || radarMethod.methodName.value.indexOf("-") > -1) { + return overloadJs; + } + var methodVarName = clzVarName + "_method_" + radarMethod.methodName.value + "_" + Math.random().toString(36).slice( - 4); + overloadJs += "var " + methodVarName + "=" + clzVarName + "." + radarMethod.methodName.value + ".overload("; + if (radarMethod.paramsNum.value > 0) { + for (var j = 0; j < radarMethod.paramsNum.value; j++) { + overloadJs += "'"; + overloadJs += radarMethod.paramsClasses.value[j]; + overloadJs += "'"; + if (j < (radarMethod.paramsNum.value - 1)) { + overloadJs += ","; + } + } + } + overloadJs += ");"; + overloadJs += methodVarName; + overloadJs += ".implementation = function("; + var paramsJs = ""; + for (var j = 0; j < radarMethod.paramsNum.value; j++) { + paramsJs += "v" + j; + if (j < (radarMethod.paramsNum.value - 1)) { + paramsJs += ","; + } + } + overloadJs += paramsJs; + overloadJs += ") {"; + var handle = "this"; + if (radarMethod.isStatic.value) { + handle = clzVarName; + } + if (handle == "this") { + overloadJs += "var executor = this.hashCode();"; + } else { + overloadJs += "var executor = 'Class';"; + } + overloadJs += "var beatText = '" + radarMethod.describe.value + "';"; + overloadJs += "var beat = newMethodBeat(beatText, executor);"; + if (radarMethod.returnClass.value != "void") { + overloadJs += "var ret = "; + } + overloadJs += methodVarName + ".call(" + handle; + if (radarMethod.paramsNum.value > 0) { + overloadJs += "," + paramsJs + ");"; + } else { + overloadJs += ");"; + } + overloadJs += "printBeat(beat);"; + if (radarMethod.returnClass.value != "void") { + overloadJs += "return ret;"; + } + overloadJs += "};"; + return overloadJs; +} + +//生成构造方法的overload +function generateFridaConstructorMethodOverload(clzVarName, constructorMethod) { + var overloadJs = ""; + if (!constructorMethod.isLocal.value) { + return overloadJs; + } + var constructorMethodVarName = clzVarName + "_init_" + Math.random().toString(36).slice( - 4); + var hookConstructorMethodJs = clzVarName + ".$init.overload("; + if (constructorMethod.paramsNum.value > 0) { + for (var j = 0; j < constructorMethod.paramsNum.value; j++) { + hookConstructorMethodJs += "'"; + hookConstructorMethodJs += constructorMethod.paramsClasses.value[j]; + hookConstructorMethodJs += "'"; + if (j < (constructorMethod.paramsNum.value - 1)) { + hookConstructorMethodJs += ","; + } + } + } + hookConstructorMethodJs += ");"; + overloadJs += "var " + constructorMethodVarName + " = " + hookConstructorMethodJs; + overloadJs += constructorMethodVarName + ".implementation = function("; + var paramsJs = ""; + for (var j = 0; j < constructorMethod.paramsNum.value; j++) { + paramsJs += "v" + j; + if (j < (constructorMethod.paramsNum.value - 1)) { + paramsJs += ","; + } + } + overloadJs += paramsJs; + overloadJs += ") {"; + overloadJs += "var executor = this.hashCode();"; + overloadJs += "var beatText = '" + constructorMethod.describe.value + "';"; + overloadJs += "var beat = newMethodBeat(beatText, executor);"; + overloadJs += "var returnObj = "; + overloadJs += constructorMethodVarName + ".call(this"; + if (constructorMethod.paramsNum.value > 0) { + overloadJs += "," + paramsJs + ");"; + } else { + overloadJs += ");"; + } + overloadJs += "printBeat(beat);"; + overloadJs += "return returnObj;};"; + return overloadJs; +} + +//RadarClassResult string +function generateMethodHookJs(radarClassResult, methodName) { + if (radarClassResult.isEnum.value || radarClassResult.isInterface.value) { + return ""; + } + var hookJs = ""; + var hasHook = false; + var clzHookJs = ""; + + var clzVarName = radarClassResult.className.value.replace(/[\.$;]/g, "_") + "_clz"; + clzHookJs += "var " + clzVarName + " = Java.use('" + radarClassResult.className.value + "');"; + var methods = radarClassResult.methods.value; + var hookMethod = (methodName == "?" || methodName == "*"); + var hookConstructorMethod = (methodName == "_" || methodName == "*"); + for (var i = 0; i < methods.length; i++) { + var radarMethod = methods[i]; + if (hookMethod || radarMethod.matchName(methodName)) { + hasHook = true; + clzHookJs += generateFridaMethodOverload(clzVarName, radarMethod); + } + } + + //是否需要hook构造方法 + if (hookConstructorMethod) { + hasHook = true; + var constructorMethods = radarClassResult.constructorMethods.value; + for (var i = 0; i < constructorMethods.length; i++) { + clzHookJs += generateFridaConstructorMethodOverload(clzVarName, constructorMethods[i]); + } + } + + if (hasHook) { + hookJs += clzHookJs; + } + return hookJs; +}; \ No newline at end of file diff --git a/js/replace_dlsym_get_pthread_create.js b/js/replace_dlsym_get_pthread_create.js deleted file mode 100644 index 8c3670d..0000000 --- a/js/replace_dlsym_get_pthread_create.js +++ /dev/null @@ -1,112 +0,0 @@ -var pthread_create_ptr = Module.getExportByName(null, "pthread_create"); - -// 备份原始函数 -var original_pthread_create = new NativeFunction(pthread_create_ptr, 'int', ['pointer', 'pointer', 'pointer', 'pointer']); - -var my_pthread_create = new NativeCallback(function (thread_ptr, attr_ptr, start_routine, arg_ptr) { - console.log("[*] 自定义 pthread_create 被调用!"); - console.log(" thread_ptr: " + thread_ptr); - console.log(" attr_ptr: " + attr_ptr); - console.log(" start_routine: " + start_routine); - console.log(" arg_ptr: " + arg_ptr); - var find_module = Process.findModuleByAddress(start_routine); - console.log("这是pthread_create传入的函数地址,你可以再去hook这个函数看看BLR X8指令的位置,然后NOP掉--> Module: " + find_module.name + " offset:" + start_routine.sub(find_module.base)); - // 你可以选择真的创建一个线程(高级场景),或者只打印/屏蔽掉 - // 此处直接返回成功状态 - return 0; -}, 'int', ['pointer', 'pointer', 'pointer', 'pointer']); - -Interceptor.attach(Module.getExportByName(null, "dlsym"), { - onEnter(args) { - this.symbol = Memory.readUtf8String(args[1]); - }, - onLeave(retval) { - if (this.symbol.indexOf("pthread_create") !== -1) { - console.log("[*] dlsym loaded pthread_create, addr:", retval); - // 获取当前线程的调用栈 - var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE); - // 获取调用该方法的地址(栈中的上一级) - var callerAddress = backtrace[0]; - var find_module = Process.findModuleByAddress(callerAddress); - if (find_module && find_module.name.indexOf("libmsaoaidsec.so") !== -1) { - // console.log('\nBacktrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) - // .map(DebugSymbol.fromAddress).join('\n')); - console.log("invoke dlsym |--> Module: " + find_module.name + " offset:" + callerAddress.sub(find_module.base)); - // 👇 替换返回值为我们自定义的 pthread_create - retval.replace(ptr(my_pthread_create)); - } - } - } -}); - -// clone(Linux下线程创建使用的系统调用)、pthread create 的相关库函数pthread join 等。这些函数也可能涉及线程的创建和管理,可以间接达到相同的效果。 -// var clone = Module.findExportByName('libc.so', 'clone'); -// Interceptor.attach(clone, { -// onEnter: function(args) { -// // args[3] 子线程的栈地址。如果这个值为 0,可能意味着没有指定栈地址 -// if(args[3] != 0){ -// var callerAddress = args[3].add(96).readPointer() -// var find_module = Process.findModuleByAddress(callerAddress); -// if (find_module && find_module.name.indexOf("libmsaoaidsec.so") !== -1) { -// // console.log('\nBacktrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) -// // .map(DebugSymbol.fromAddress).join('\n')); -// console.log("hook_clone invoke Module: " + find_module.name + " offset:" + callerAddress.sub(find_module.base)); -// // 👇 替换返回值为我们自定义的 pthread_create -// args[3] = ptr(my_pthread_create); -// } -// } -// }, -// onLeave: function(retval) { -// } -// }); -// -// function anti_check_frida_feature() { -// var pt_strstr = Module.findExportByName("libc.so", 'strstr'); -// var pt_strcmp = Module.findExportByName("libc.so", 'strcmp'); -// -// Interceptor.attach(pt_strstr, { -// onEnter: function (args) { -// var str1 = args[0].readCString(); -// var str2 = args[1].readCString(); -// if ( -// str2.indexOf("REJECT") !== -1 || -// str2.indexOf("tmp") !== -1 || -// str2.indexOf("frida") !== -1 || -// str2.indexOf("gum-js-loop") !== -1 || -// str2.indexOf("gmain") !== -1 || -// str2.indexOf("linjector") !== -1 -// ) { -// //console.log("strstr-->", str1, str2); -// this.hook = true; -// } -// }, onLeave: function (retval) { -// if (this.hook) { -// retval.replace(0); -// } -// } -// }); -// -// Interceptor.attach(pt_strcmp, { -// onEnter: function (args) { -// var str1 = args[0].readCString(); -// var str2 = args[1].readCString(); -// if ( -// str2.indexOf("REJECT") !== -1 || -// str2.indexOf("tmp") !== -1 || -// str2.indexOf("frida") !== -1 || -// str2.indexOf("gum-js-loop") !== -1 || -// str2.indexOf("gmain") !== -1 || -// str2.indexOf("linjector") !== -1 -// ) { -// //console.log("strcmp-->", str1, str2); -// this.hook = true; -// } -// }, onLeave: function (retval) { -// if (this.hook) { -// retval.replace(0); -// } -// } -// }) -// } -// -// setImmediate(anti_check_frida_feature) \ No newline at end of file diff --git a/js/rpc.js b/js/rpc.js index e7caf90..46b59e0 100644 --- a/js/rpc.js +++ b/js/rpc.js @@ -41,221 +41,48 @@ function checkFile(filepath, checkLength) { return file.exists() && file.length() == checkLength; }; - -function discoverClass(className) { - if (!className) { - return; - } - var radarClz = Java.use("gz.radar.ClassRadar"); - var radarClassResult = radarClz.discoverClass(className); - return radarClassResult; -}; - -function generateFridaMethodOverload(clzVarName, radarMethod) { - var overloadJs = ""; - if (!radarMethod.isLocal.value || radarMethod.methodName.value.indexOf("-") > -1) { - return overloadJs; - } - var methodVarName = clzVarName + "_method_" + radarMethod.methodName.value + "_" + Math.random().toString(36).slice( - 4); - overloadJs += "var " + methodVarName + "=" + clzVarName + "." + radarMethod.methodName.value + ".overload("; - if (radarMethod.paramsNum.value > 0) { - for (var j = 0; j < radarMethod.paramsNum.value; j++) { - overloadJs += "'"; - overloadJs += radarMethod.paramsClasses.value[j]; - overloadJs += "'"; - if (j < (radarMethod.paramsNum.value - 1)) { - overloadJs += ","; - } - } - } - overloadJs += ");"; - overloadJs += methodVarName; - overloadJs += ".implementation = function("; - var paramsJs = ""; - for (var j = 0; j < radarMethod.paramsNum.value; j++) { - paramsJs += radarMethod.parameterNames.value[j]; - if (j < (radarMethod.paramsNum.value - 1)) { - paramsJs += ","; - } - } - overloadJs += paramsJs; - overloadJs += ") {"; - var handle = "this"; - if (radarMethod.isStatic.value) { - handle = clzVarName; - } - if (handle == "this") { - overloadJs += "var executor = this.hashCode();"; - } else { - overloadJs += "var executor = 'Class';"; - } - overloadJs += "var beatText = '" + radarMethod.describe.value + "';"; - overloadJs += "var beat = newMethodBeat(beatText, executor);"; - if (radarMethod.returnClass.value != "void") { - overloadJs += "var ret = "; - } - overloadJs += methodVarName + ".call(" + handle; - if (radarMethod.paramsNum.value > 0) { - overloadJs += "," + paramsJs + ");"; - } else { - overloadJs += ");"; - } - overloadJs += "printBeat(beat);"; - if (radarMethod.returnClass.value != "void") { - overloadJs += "return ret;"; - } - overloadJs += "};"; - return overloadJs; -} - -//生成构造方法的overload -function generateFridaConstructorMethodOverload(clzVarName, constructorMethod) { - var overloadJs = ""; - if (!constructorMethod.isLocal.value) { - return overloadJs; - } - var constructorMethodVarName = clzVarName + "_init_" + Math.random().toString(36).slice( - 4); - var hookConstructorMethodJs = clzVarName + ".$init.overload("; - if (constructorMethod.paramsNum.value > 0) { - for (var j = 0; j < constructorMethod.paramsNum.value; j++) { - hookConstructorMethodJs += "'"; - hookConstructorMethodJs += constructorMethod.paramsClasses.value[j]; - hookConstructorMethodJs += "'"; - if (j < (constructorMethod.paramsNum.value - 1)) { - hookConstructorMethodJs += ","; - } - } - } - hookConstructorMethodJs += ");"; - overloadJs += "var " + constructorMethodVarName + " = " + hookConstructorMethodJs; - overloadJs += constructorMethodVarName + ".implementation = function("; - var paramsJs = ""; - for (var j = 0; j < constructorMethod.paramsNum.value; j++) { - paramsJs += "v" + j; - if (j < (constructorMethod.paramsNum.value - 1)) { - paramsJs += ","; - } - } - overloadJs += paramsJs; - overloadJs += ") {"; - overloadJs += "var executor = this.hashCode();"; - overloadJs += "var beatText = '" + constructorMethod.describe.value + "';"; - overloadJs += "var beat = newMethodBeat(beatText, executor);"; - overloadJs += "var returnObj = "; - overloadJs += constructorMethodVarName + ".call(this"; - if (constructorMethod.paramsNum.value > 0) { - overloadJs += "," + paramsJs + ");"; - } else { - overloadJs += ");"; - } - overloadJs += "printBeat(beat);"; - overloadJs += "return returnObj;};"; - return overloadJs; -} - -//RadarClassResult string -function generateMethodHookJs(radarClassResult, methodName) { - if (radarClassResult.isEnum.value || radarClassResult.isInterface.value) { - return ""; - } - var hookJs = ""; - var hasHook = false; - var clzHookJs = ""; - - var clzVarName = radarClassResult.className.value.replace(/[\.$;]/g, "_") + "_clz"; - clzHookJs += "var " + clzVarName + " = Java.use('" + radarClassResult.className.value + "');"; - var methods = radarClassResult.methods.value; - var hookMethod = (methodName == "?" || methodName == "*"); - var hookConstructorMethod = (methodName == "_" || methodName == "*"); - for (var i = 0; i < methods.length; i++) { - var radarMethod = methods[i]; - if (hookMethod || radarMethod.matchName(methodName)) { - hasHook = true; - clzHookJs += generateFridaMethodOverload(clzVarName, radarMethod); - } - } - - //是否需要hook构造方法 - if (hookConstructorMethod) { - hasHook = true; - var constructorMethods = radarClassResult.constructorMethods.value; - for (var i = 0; i < constructorMethods.length; i++) { - clzHookJs += generateFridaConstructorMethodOverload(clzVarName, constructorMethods[i]); - } - } - - if (hasHook) { - hookJs += clzHookJs; - } - return hookJs; -}; - -function class_exists(className) { - var exists = false; - try { - var clz = Java.use(className); - exists = true; - } catch(err) { - //console.log(err); - } - return exists; -}; - //可能会超时 为了防止这个发生,可以在函数 setImmediate 中给你的脚本添加一层包装 rpc.exports = { - starthttpserver: function (dex_file, allClz) { - var result = ""; + loaddex: function(checkClassName, dexPath) { Java.perform(function() { - if (dex_file) { - var DexClassLoader = Java.use("dalvik.system.DexClassLoader"); - var ActivityThread = Java.use("android.app.ActivityThread"); - var app = ActivityThread.currentApplication(); - var context = app.getApplicationContext(); - var cacheDir = context.getCodeCacheDir().getAbsolutePath(); - var parent = context.getClassLoader(); - var dexPath = "/data/local/tmp/radar.dex:" + dex_file; - console.log("dexPath", dexPath); - var newLoader = DexClassLoader.$new( - dexPath, - cacheDir, - null, - parent - ); - Java.classFactory.loader = newLoader; - var httpServerBoot = Java.use('gz.httpserver.HookerWebServerBoot'); - var ArrayList = Java.use("java.util.ArrayList"); - // JS 里分割 - var arr = allClz.split(","); - // 创建 ArrayList - var clzList = ArrayList.$new(); - // 填充 ArrayList - for (var i = 0; i < arr.length; i++) { - clzList.add(arr[i]); - } - result = httpServerBoot.scanAndStartHttpServer(clzList); - }else{ - Java.openClassFile("/data/local/tmp/radar.dex").load(); - var httpServerBoot2 = Java.use('gz.httpserver.HookerWebServerBoot'); - result = httpServerBoot2.startDefaultHttpServer(); + if (!classExists(checkClassName)) { + Java.openClassFile(dexPath).load(); } }); + }, + containsclass: function(className) { + var result = false; + Java.perform(function() { + result = classExists(className); + }); return result; }, - loadradardex: function() { + so: function(moduleName) { + return findSOLibrary(moduleName); + }, + findclasses: function(classRegex) { + var report = ""; Java.perform(function() { - if (!class_exists("gz.radar.ClassRadar")) { - var context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(); - var packageName = context.getPackageName(); - Java.openClassFile('/data/local/tmp/radar.dex').load(); + var radarClassResults = findClasses(classRegex); + report += ("Found Classes: " + radarClassResults.length) + "\n"; + for (var j = 0; j < radarClassResults.length; j++) { + var radarClassResult = radarClassResults[j]; + report += ("\t" + radarClassResult.describ()) + "\n"; } }); + return report; }, - containsclass: function(className) { - var result = false; + findclasses2: function(className) { + var report = ""; Java.perform(function() { - result = class_exists(className); + var radarClassResults = findOffspringsClasses(className.trim()); + report += ("Found Classes: " + radarClassResults.length) + "\n"; + for (var j = 0; j < radarClassResults.length; j++) { + var radarClassResult = radarClassResults[j]; + report += ("\t" + radarClassResult.describ()) + "\n"; + } }); - return result; + return report; }, hookjs: function(className, toSpace) { var found = true; @@ -273,7 +100,7 @@ rpc.exports = { } }); if (found) { - return hookJs; + return jsbeautify(hookJs); } return ""; }, @@ -357,54 +184,28 @@ rpc.exports = { }, objectinfo: function(objectId) { var report = ""; - if (class_exists(objectId)) { - //判断是否是类名 - var max = 10; - var found = []; - var class_name = objectId; - Java.perform(function () { - var ObjectsStore = Java.use("gz.radar.objects.ObjectsStore"); - Java.choose(class_name, { - onMatch: function (instance) { - if (found.length >= max) { - // 已达上限,直接忽略后续回调 - console.warn("The upper limit has been reached."); - return; - } - found.push(class_name); - var newObjectId = ObjectsStore.storeObject(instance); - console.log("Found " + class_name + " instance: " + instance + " ObjectId: " + newObjectId); - }, - onComplete: function () { - console.log("Search complete. Please continue exploring using object with [ObjectId]"); - } - }); - }); - }else{ - //不是类名就是object_id - Java.perform(function() { - var radarAndroidClz = Java.use("gz.radar.Android"); - var objectInfo = radarAndroidClz.getObjectInfo(objectId); - if (!objectInfo) { - report += "Not Found Any Object." - return; - } - report += ("------------------Object--------------------") + "\n"; - report += ("Object Class: " + objectInfo.getName()) + "\n"; - report += ("Object SuperClass: " + objectInfo.getSuperClazz()) + "\n"; - report += ("Object ImplementInterfaces: " + objectInfo.getImplementInterfaces()) + "\n"; - var androidApkFields = objectInfo.getAndroidApkFields(); - report += ("Object Fields: " + androidApkFields.length) + "\n"; - for (var j = 0; j < androidApkFields.length; j++) { - report += ("\t" + androidApkFields[j].toLine()) + "\n"; - } - var methods = objectInfo.methods(); - report += ("Object Methods: " + methods.length) + "\n"; - for (var j = 0; j < methods.length; j++) { - report += ("\t" + methods[j]) + "\n"; - } - }); - } + Java.perform(function() { + var radarAndroidClz = Java.use("gz.radar.Android"); + var objectInfo = radarAndroidClz.getObjectInfo(objectId); + if (!objectInfo) { + report += "Not Found Any Object." + return; + } + report += ("------------------Object--------------------") + "\n"; + report += ("Object Class: " + objectInfo.getName()) + "\n"; + report += ("Object SuperClass: " + objectInfo.getSuperClazz()) + "\n"; + report += ("Object ImplementInterfaces: " + objectInfo.getImplementInterfaces()) + "\n"; + var androidApkFields = objectInfo.getAndroidApkFields(); + report += ("Object Fields: " + androidApkFields.length) + "\n"; + for (var j = 0; j < androidApkFields.length; j++) { + report += ("\t" + androidApkFields[j].toLine()) + "\n"; + } + var methods = objectInfo.methods(); + report += ("Object Methods: " + methods.length) + "\n"; + for (var j = 0; j < methods.length; j++) { + report += ("\t" + methods[j]) + "\n"; + } + }); return report; }, objecttoexplain: function(objectId) { @@ -477,19 +278,4 @@ rpc.exports = { }); return mainactivityName; }, - cleanup: function () { - // 清理所有拦截器 - Interceptor.detachAll(); - // 如果你设置了定时器或 Stalker,也可以清理 - Stalker.unfollow(); - // clearInterval(...); - } -}; - -Java.perform(function() { - if (!class_exists("gz.radar.ClassRadar")) { - var context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(); - var packageName = context.getPackageName(); - Java.openClassFile('/data/local/tmp/radar.dex').load(); - } -}); \ No newline at end of file +}; \ No newline at end of file diff --git a/js/spoof_signature.js b/js/spoof_signature.js deleted file mode 100644 index f0554b2..0000000 --- a/js/spoof_signature.js +++ /dev/null @@ -1,19 +0,0 @@ -function spoofSignature() { - const originalSignature = "" //This will be set by patch_apk.py - Java.perform(() => { - const PackageManager = Java.use("android.app.ApplicationPackageManager"); - const Signature = Java.use("android.content.pm.Signature"); - const ActivityThread = Java.use('android.app.ActivityThread'); - PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(a, b) { - const packageInfo = this.getPackageInfo(a, b); - const context = ActivityThread.currentApplication().getApplicationContext(); - const name = context.getPackageName(); - if (a == name && b == 64) { - const signature = Signature.$new(originalSignature); - packageInfo.signatures.value = Java.array('android.content.pm.Signature', [signature]); - } - return packageInfo; - } - }); -} - diff --git a/js/string_hooker.js b/js/string_hooker.js new file mode 100644 index 0000000..eee8742 --- /dev/null +++ b/js/string_hooker.js @@ -0,0 +1,83 @@ +function methodInBeat(invokeId, timestamp, methodName, executor) { + var startTime = timestamp; + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var threadClz = Java.use("java.lang.Thread"); + var currentThread = threadClz.currentThread(); + var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()); + var str = ("------------startFlag:" + invokeId + ",objectHash:"+executor+",thread(id:" + currentThread.getId() +",name:" + currentThread.getName() + "),timestamp:" + startTime+"---------------\n"); + str += methodName + "\n"; + str += stackInfo.substring(20); + str += ("------------endFlag:" + invokeId + ",usedtime:" + (new Date().getTime() - startTime) +"---------------\n"); + console.log(str); +}; + +function log(str) {console.log(str);}; + +//https://github.com/frida/frida/issues/503 bug补丁 +function stringBuilderToStringPatch(stringBuilderObj) { + var str = ''; + var length = stringBuilderObj.length(); + for (var i = 0; i < length; i++) { + str += stringBuilderObj.charAt(i); + } + return str; +} + +function getHead(str, len) { + if (str.length > len) { + return str.substring(0, len) + "...... length:" + str.length; + } else { + return str; + } +} + +var containRegExps = new Array(RegExp(/惊雷/)) + +var notContainRegExps = new Array(RegExp(/\.jpg$/)) + +function check(stringJavaObject) { + var str = stringJavaObject.toString(); + if (! (str && str.match)) { + return false; + } + for (var i = 0; i < containRegExps.length; i++) { + if (!str.match(containRegExps[i])) { + return false; + } + } + for (var i = 0; i < notContainRegExps.length; i++) { + if (str.match(notContainRegExps[i])) { + return false; + } + } + return true; +} + +Java.perform(function() { + var androidLogClz = Java.use("android.util.Log"); + var exceptionClz = Java.use("java.lang.Exception"); + var java_lang_StringBuilder_clz = Java.use('java.lang.StringBuilder'); + var java_lang_StringBuilder_clz_toString_9ogt = java_lang_StringBuilder_clz.toString.overload(); + java_lang_StringBuilder_clz_toString_9ogt.implementation = function() { + var thErr = undefined; + var stringJavaObject = undefined; + try { + var startTime = new Date().getTime(); + stringJavaObject = java_lang_StringBuilder_clz_toString_9ogt.call(this); + if (check(stringJavaObject)) { + var headTxt = getHead(stringJavaObject, 500); + var invokeId = Math.random().toString(36).slice( - 8); + var executor = this.hashCode(); + methodInBeat(invokeId, startTime, "Detecting " + headTxt + " in stacks:", executor); + } + } catch(err) { + thErr = err; + } finally { + if (thErr) { + stringJavaObject = stringBuilderToStringPatch(this); + } + return stringJavaObject; + } + }; +}); \ No newline at end of file diff --git a/js/trace_init_proc.js b/js/trace_init_proc.js deleted file mode 100644 index 624faed..0000000 --- a/js/trace_init_proc.js +++ /dev/null @@ -1,95 +0,0 @@ -var hasAlreadyHooked = false; - -//init_proc func start addr -var startAddr = null; - -//init_proc func end addr -var endAddr = null; - -// replace your so name eg: xxx.so -var somodule = "xxx.so"; - -function hook_dlopen(){ - //Android8.0之后加载so通过android_dlopen_ext函数 - var android_dlopen_ext = Module.findExportByName(null,"android_dlopen_ext"); - console.log("addr_android_dlopen_ext",android_dlopen_ext); - Interceptor.attach(android_dlopen_ext,{ - onEnter:function(args){ - var pathptr = args[0]; - if(pathptr!=null && pathptr != undefined){ - var path = ptr(pathptr).readCString(); - this.path = path; - if(path.indexOf(somodule)!=-1){ - console.log("android_dlopen_ext:",path); - trace_init_proc() - } - } - }, - onLeave:function(retvel){ - console.log(this.path+" leave!"); - } - }) -} - -function trace_init_proc() { - let linker = null; - if (Process.pointerSize === 4) { - linker = Process.findModuleByName("linker"); - } else { - linker = Process.findModuleByName("linker64"); - } - let call_constructors_addr, get_soname - let symbols = linker.enumerateSymbols(); - for (let index = 0; index < symbols.length; index++) { - let symbol = symbols[index]; - if (symbol.name === "__dl__ZN6soinfo17call_constructorsEv") { - call_constructors_addr = symbol.address; - } else if (symbol.name === "__dl__ZNK6soinfo10get_sonameEv") { - get_soname = new NativeFunction(symbol.address, "pointer", ["pointer"]); - } - } - console.log("call_constructors_addr:" + call_constructors_addr); - var listener = Interceptor.attach(call_constructors_addr, { - onEnter: function (args) { - //console.log("hooked call_constructors") - var module = Process.findModuleByName(somodule) - if (module != null && !hasAlreadyHooked) { - hasAlreadyHooked = true; - console.log(somodule + " base address:", module.base); - var targetAddress = module.base.add(startAddr); - var absolutStartAddr = targetAddress; - var absolutEndAddr = module.base.add(endAddr); - console.log(`init_proc func starts at ${absolutStartAddr} ends at ${absolutEndAddr}`); - Interceptor.attach(targetAddress, { - onEnter: function(args) { - console.log(`init_proc called at ${targetAddress}`); - // 开启 Stalker 跟踪 - Stalker.follow(this.threadId, { - transform: function (iterator) { - var instruction; - while ((instruction = iterator.next()) !== null) { - var addr = instruction.address; - if (addr.compare(absolutStartAddr) >= 0 && addr.compare(absolutEndAddr) <= 0) { - console.log("[EXEC] 0x" + (addr - module.base).toString(16) + ": " + instruction.toString()); - } - iterator.keep(); - } - } - }); - }, - onLeave: function(retval) { - console.log("Function returned:", retval); - // 可以修改返回值 - // retval.replace(ptr(0x1)); - } - }); - } - - }, - }) -} -function main(){ - hook_dlopen() -} - -main() \ No newline at end of file diff --git a/js/webview_enable_debug.js b/js/webview_enable_debug.js deleted file mode 100644 index d9d632b..0000000 --- a/js/webview_enable_debug.js +++ /dev/null @@ -1,55 +0,0 @@ -function main() { - Java.perform(function () { - var WebView = Java.use('android.webkit.WebView'); - var Overloads = [ - ['android.content.Context'], - ['android.content.Context', 'android.util.AttributeSet'], - ['android.content.Context', 'android.util.AttributeSet', 'int'], - ['android.content.Context', 'android.util.AttributeSet', 'int', 'int'], - ['android.content.Context', 'android.util.AttributeSet', 'int', 'boolean'], - ['android.content.Context', 'android.util.AttributeSet', 'int', 'java.util.Map', 'boolean'], - ['android.content.Context', 'android.util.AttributeSet', 'int', 'int', 'java.util.Map', 'boolean'] - ]; - - Overloads.forEach(function (args) { - var overload = WebView.$init.overload.apply(WebView.$init, args); - overload.implementation = function () { - var result = overload.apply(this, arguments); - - console.log('\n[+] WebView constructor called with args: ' + JSON.stringify(args)); - console.log('[+] WebView instance: ' + this); - - try { - var Log = Java.use("android.util.Log"); - var Throwable = Java.use("java.lang.Throwable"); - console.log('[*] Stack trace:\n' + Log.getStackTraceString(Throwable.$new())); - } catch (e) { - console.log("[-] Failed to get stack trace: " + e); - } - - this.setWebContentsDebuggingEnabled(true); - console.log('[+] WebContents debugging enabled.\n'); - - return result; - }; - }); - - // Hook evaluateJavascript - WebView.evaluateJavascript.overload('java.lang.String', 'android.webkit.ValueCallback').implementation = function (script, callback) { - console.log('\n[📡] evaluateJavascript called!'); - console.log('[📝] Script: \n' + script); - - try { - var Log = Java.use("android.util.Log"); - var Throwable = Java.use("java.lang.Throwable"); - console.log('[*] Stack trace:\n' + Log.getStackTraceString(Throwable.$new())); - } catch (e) { - console.log("[-] Failed to get stack trace: " + e); - } - - return this.evaluateJavascript(script, callback); - }; - }); -} - -setImmediate(main); \ No newline at end of file diff --git a/mobile-deploy/daemon_app.sh b/mobile-deploy/daemon_app.sh deleted file mode 100644 index 0a71bad..0000000 --- a/mobile-deploy/daemon_app.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/system/bin/sh - -PKG="$1" - -if [ -z "$PKG" ]; then - echo "Usage: $0 " - exit 1 -fi - -while true -do - if ! pidof $PKG >/dev/null; then - echo "[$(date)] restart $PKG" - - monkey -p $PKG -c android.intent.category.LAUNCHER 1 - - # 等待APP启动 - sleep 15 - else - echo "[$(date)] $PKG alive" - sleep 3 - fi -done \ No newline at end of file diff --git a/mobile-deploy/deploy.sh b/mobile-deploy/deploy.sh new file mode 100644 index 0000000..71b1b7a --- /dev/null +++ b/mobile-deploy/deploy.sh @@ -0,0 +1,89 @@ +print "disable android firewall." +iptables -P INPUT ACCEPT +iptables -P OUTPUT ACCEPT +iptables -P FORWARD ACCEPT +ip6tables -P INPUT ACCEPT +ip6tables -P OUTPUT ACCEPT +ip6tables -P FORWARD ACCEPT +cd /sdcard +dir=/data/mobile-deploy +if [ ! -d $dir ]; then + mkdir /data/mobile-deploy +fi +cp /sdcard/mobile-deploy/hluda-server* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/tcpforward* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/frpc* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/busybox* /data/mobile-deploy/ +chmod 700 /data/mobile-deploy/* + +cpuInfo=$(cat /proc/cpuinfo) +currentTime=$(date +%s) + +if [[ $cpuInfo == *AArch64* ]] || [[ $cpuInfo == *ARMv8* ]] || [[ $cpuInfo == *ARMv9* ]]; then + abiType="arm64" +elif [[ $cpuInfo == *ARMv7* ]]; then + abiType="arm" +elif [[ $cpuInfo == *Intel* ]] || [[ $cpuInfo == *AMD* ]]; then + abiType="x86" +else + abiType="arm64" +fi + +echo "" > /data/mobile-deploy/tools_env.rc +chmod 700 /data/mobile-deploy/tools_env.rc + +firda_server_bind_port=27042 + +if [ -n "$1" ]; then + firda_server_bind_port=$1 + echo "set firda_server_bind_port to $1" +fi + +if [[ $abiType == arm64 ]]; then + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-arm64 -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_arm64" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_arm64" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-armv7m vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-armv7m telnet'" >> /data/mobile-deploy/tools_env.rc + +elif [[ $abiType == arm ]]; then + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-arm -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_arm" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_arm" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-armv7m vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-armv7m telnet'" >> /data/mobile-deploy/tools_env.rc +else + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-x86 -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_x86" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_x86" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-i686 vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-i686 telnet'" >> /data/mobile-deploy/tools_env.rc +fi + +echo "alias ll='ls -l'" >> /data/mobile-deploy/tools_env.rc + +echo "start network adb." +setprop service.adb.tcp.port 5555 +nohup stop adbd && start adbd > /sdcard/adbd.log 2>&1 & +echo "deploy successfull." + +#如果手机的magisk root的话使用以下命令改为全局debug模式 +#magisk resetprop ro.debuggable 1 +#stop;start; + + diff --git a/mobile-deploy/deploy2.sh b/mobile-deploy/deploy2.sh new file mode 100644 index 0000000..f96ee51 --- /dev/null +++ b/mobile-deploy/deploy2.sh @@ -0,0 +1,79 @@ +cd /sdcard +dir=/data/mobile-deploy +if [ ! -d $dir ]; then + mkdir /data/mobile-deploy +fi +cp /sdcard/mobile-deploy/hluda-server* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/tcpforward* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/frpc* /data/mobile-deploy/ +cp /sdcard/mobile-deploy/busybox* /data/mobile-deploy/ +chmod 700 /data/mobile-deploy/* + +cpuInfo=$(cat /proc/cpuinfo) +currentTime=$(date +%s) + +if [[ $cpuInfo == *AArch64* ]] || [[ $cpuInfo == *ARMv8* ]] || [[ $cpuInfo == *ARMv9* ]]; then + abiType="arm64" +elif [[ $cpuInfo == *ARMv7* ]]; then + abiType="arm" +elif [[ $cpuInfo == *Intel* ]] || [[ $cpuInfo == *AMD* ]]; then + abiType="x86" +else + abiType="arm64" +fi + +echo "" > /data/mobile-deploy/tools_env.rc +chmod 700 /data/mobile-deploy/tools_env.rc + +firda_server_bind_port=27042 + +if [ -n "$1" ]; then + firda_server_bind_port=$1 + echo "set firda_server_bind_port to $1" +fi + +if [[ $abiType == arm64 ]]; then + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-arm64 -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_arm64" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_arm64" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-armv7m vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-armv7m telnet'" >> /data/mobile-deploy/tools_env.rc + +elif [[ $abiType == arm ]]; then + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-arm -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_arm" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_arm" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-armv7m vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-armv7m telnet'" >> /data/mobile-deploy/tools_env.rc +else + echo "start frida-server" + nohup /data/mobile-deploy/hluda-server-14.2.13-android-x86 -l 0.0.0.0:$firda_server_bind_port > /sdcard/frida-server.log 2>&1 & + + echo "alias tcpforward=/data/mobile-deploy/tcpforward_linux_x86" >> /data/mobile-deploy/tools_env.rc + + echo "alias frpc=/data/mobile-deploy/frpc_x86" >> /data/mobile-deploy/tools_env.rc + + echo "alias vi='/data/mobile-deploy/busybox-i686 vi'" >> /data/mobile-deploy/tools_env.rc + + echo "alias telnet='/data/mobile-deploy/busybox-i686 telnet'" >> /data/mobile-deploy/tools_env.rc +fi + +echo "alias ll='ls -l'" >> /data/mobile-deploy/tools_env.rc + +echo "deploy successfull." + +#如果手机的magisk root的话使用以下命令改为全局debug模式 +#magisk resetprop ro.debuggable 1 +#stop;start; + + diff --git a/mobile-deploy/frida-server-16.7.19-android-arm b/mobile-deploy/hluda-server-14.2.13-android-arm similarity index 61% rename from mobile-deploy/frida-server-16.7.19-android-arm rename to mobile-deploy/hluda-server-14.2.13-android-arm index b306e9f..531ca94 100644 Binary files a/mobile-deploy/frida-server-16.7.19-android-arm and b/mobile-deploy/hluda-server-14.2.13-android-arm differ diff --git a/mobile-deploy/frida-server-16.7.19-android-arm64 b/mobile-deploy/hluda-server-14.2.13-android-arm64 similarity index 67% rename from mobile-deploy/frida-server-16.7.19-android-arm64 rename to mobile-deploy/hluda-server-14.2.13-android-arm64 index ea58bfb..18e3b55 100644 Binary files a/mobile-deploy/frida-server-16.7.19-android-arm64 and b/mobile-deploy/hluda-server-14.2.13-android-arm64 differ diff --git a/mobile-deploy/hluda-server-14.2.13-android-x86 b/mobile-deploy/hluda-server-14.2.13-android-x86 new file mode 100644 index 0000000..4fc16ab Binary files /dev/null and b/mobile-deploy/hluda-server-14.2.13-android-x86 differ diff --git a/mobile-deploy/libevent-2.1.so b/mobile-deploy/libevent-2.1.so deleted file mode 100644 index 33ff434..0000000 Binary files a/mobile-deploy/libevent-2.1.so and /dev/null differ diff --git a/mobile-deploy/libevent_core-2.1.so b/mobile-deploy/libevent_core-2.1.so deleted file mode 100644 index 57a6694..0000000 Binary files a/mobile-deploy/libevent_core-2.1.so and /dev/null differ diff --git a/mobile-deploy/libext.so b/mobile-deploy/libext.so deleted file mode 100755 index 5bff5ab..0000000 Binary files a/mobile-deploy/libext.so and /dev/null differ diff --git a/mobile-deploy/libext64.so b/mobile-deploy/libext64.so deleted file mode 100755 index 50d339a..0000000 Binary files a/mobile-deploy/libext64.so and /dev/null differ diff --git a/mobile-deploy/radar.dex b/mobile-deploy/radar.dex deleted file mode 100644 index 2b17dc6..0000000 Binary files a/mobile-deploy/radar.dex and /dev/null differ diff --git a/mobile-deploy/redsocks b/mobile-deploy/redsocks deleted file mode 100644 index 4803549..0000000 Binary files a/mobile-deploy/redsocks and /dev/null differ diff --git a/mobile-deploy/redsocks.conf b/mobile-deploy/redsocks.conf deleted file mode 100644 index 92919ef..0000000 --- a/mobile-deploy/redsocks.conf +++ /dev/null @@ -1,15 +0,0 @@ -base { - log_debug = on; - log_info = on; - daemon = on; - redirector = iptables; -} - -redsocks { - local_ip = 127.0.0.1; - local_port = 12345; - ip = 10.112.98.171; - port = 9998; - type = socks5; -} - diff --git a/radar.dex b/radar.dex new file mode 100644 index 0000000..e313275 Binary files /dev/null and b/radar.dex differ diff --git a/requirements.txt b/requirements.txt index 031bc65..e8c4cbb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,28 @@ -frida==16.7.19 -frida-tools==13.7.1 -adbutils==1.2.11 -androguard==3.3.5 -jsbeautifier==1.15.4 -gitpython -loguru \ No newline at end of file +certifi==2020.12.5 +chardet==4.0.0 +cli-helpers==2.1.0 +click==7.1.2 +colorama==0.4.4 +configobj==5.0.6 +delegator.py==0.1.1 +Flask==1.1.2 +frida==14.2.2 +frida-tools==9.1.0 +idna==2.10 +itsdangerous==1.1.0 +Jinja2==2.11.3 +litecli==1.5.0 +MarkupSafe==1.1.1 +objection==1.9.6 +pexpect==4.8.0 +prompt-toolkit==3.0.16 +ptyprocess==0.7.0 +Pygments==2.8.0 +requests==2.25.1 +six==1.15.0 +sqlparse==0.4.1 +tabulate==0.8.9 +terminaltables==3.1.0 +urllib3==1.26.5 +wcwidth==0.2.5 +Werkzeug==1.0.1 diff --git a/run_env.py b/run_env.py new file mode 100644 index 0000000..a1a2e90 --- /dev/null +++ b/run_env.py @@ -0,0 +1,122 @@ +''' +Created on 2020年4月20日 + +@author: stephen +''' + +import io +import os +import re + +def readJs(filename): + return io.open('./js/' + filename,'r',encoding= 'utf8').read() + +def readFile(filename): + return io.open(filename,'r',encoding= 'utf8').read() + +base_jscode = readJs("base.js") + +loadxinit_dexfile_template_jscode = """ + +var loadedXRadar = false; + +function loadXinitDexfile(dexfile) { + loadDexfile('/data/user/0/{PACKAGENAME}/xinit/'+dexfile); +}; + +function loadXRadarDexfile() { + loadedXRadar = true; + loadDexfile('/data/user/0/{PACKAGENAME}/radar.dex'); +}; + +function fastTojson(javaObject) { + var JSONClz = Java.use("gz.com.alibaba.fastjson.JSON"); + return JSONClz.toJSONString(javaObject); +}; + +function getPrettyString(javaObject) { + var XPretty = Java.use("gz.util.XPretty"); + return XPretty.getPrettyString(javaObject); +}; + +function xPretty(javaObject) { + var str = getPrettyString(javaObject); + console.log(str); +}; + +function getField(javaObject, fieldName) { + var X = Java.use("gz.util.X"); + return X.getField(javaObject, fieldName); +}; + +function storeObjectAndLog(javaObject) { + try { + var className = getClassName(javaObject); + var ObjectsStore = Java.use("gz.radar.objects.ObjectsStore"); + var objectId = ObjectsStore.storeObject(javaObject); + console.log(className + " ObjectsStoreId: " +objectId); + } catch (error) { + console.error("你传的不是一个有效的java对象!"); + } +}; + +""" + +radar_jscode = readJs("radar.js") + +jsfmt_jscode = readJs("jsfmt.js") + +string_hooker_jscode = readJs("string_hooker.js") + +rpc_jscode = base_jscode + radar_jscode + jsfmt_jscode + readJs("rpc.js") + +url_jscode = readJs("url.js") + +android_ui_jscode = readJs("android_ui.js") + +edit_text_jscode = readJs("edit_text.js") + +text_view_jscode = readJs("text_view.js") + +click_jscode = readJs("click.js") + +activity_events_jscode = readJs("activity_events.js") + +object_store_jscode = readJs("object_store.js") + +keystore_dump_jscode = readJs("keystore_dump.js") + +ssl_log_jscode = readJs("ssl_log.js") + +just_trust_me_jscode = readJs("just_trust_me.js") + +just_trust_me_okhttp_hook_finder_jscode = readJs("just_trust_me_okhttp_hook_finder.js") + +hook_RN_jscode = readJs("hook_RN.js") + +dump_dex_jscode = readJs("dump_dex.js") + +def getPythonLaucher(): + pythonLaucher = os.environ.get("__PYVENV_LAUNCHER__") + if not pythonLaucher: + pythonLaucher = os.environ.get("_", "/usr/bin/python3") + return pythonLaucher + + +xinitPyScript = "#! " + getPythonLaucher() + "\n\n" + readFile("xinitdeploy.py") +disableSslPinningPyScript = "#! " + getPythonLaucher() + "\n\n" + readFile("disable_sslpinning.py") +spiderPyScript = readFile("spider.py") + +def getRemoteDriver(): + text = readFile(".hooker_driver") + if not text: + return None + searchResult = re.search('\d+\.\d+\.\d+\.\d+:\d+', text) + if searchResult: + return searchResult.group() + return None + +def init(packageName): + if packageName == None: + return + diff --git a/spider.py b/spider.py new file mode 100644 index 0000000..1de5ae9 --- /dev/null +++ b/spider.py @@ -0,0 +1,97 @@ +import frida +import os +import io +import traceback +import time + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +jscode = io.open('wxspider.js', 'r', encoding= 'utf8').read() + +jscode += """ +rpc.exports = { + log: function(str) { + console.log(str); + }, +}; +""" + +def runCommand(cmd): + print(cmd); + lines = os.popen(cmd).readlines(); + for line in lines: + print(line.strip()); + +def on_message(message, data): + pass + +def attach(packageName, hookerDriverFridaServer, runningTime): + hasException = False + online_session = None + online_script = None + try: + rdev = frida.get_device_manager().add_remote_device(hookerDriverFridaServer) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(jscode) + online_script.on('message', on_message) + online_script.load() + for i in range(1, runningTime): + if i % 2 == 0: + online_script.exports.log(mobileIP + " spider has running been " + str(i) + " seconds."); + time.sleep(1) + except Exception: + hasException = True + warn(traceback.format_exc()) + finally: + if online_session != None and hasException == False: + online_session.detach() + return online_session,online_script + +def restartApp(packageName, mobileIP, adb): + try: + runCommand(adb + " connect "+mobileIP); + time.sleep(3) + runCommand(adb + " -s "+mobileIP+" shell am force-stop " + appPackageName); + time.sleep(5) + runCommand(adb + " -s "+mobileIP+" shell am start " + appPackageName + "/" + mainActivity); + time.sleep(20) + runCommand(adb + " disconnect "+mobileIP); + except Exception: + warn(traceback.format_exc()) + +appPackageName = "com.tencent.mm" +mainActivity = "com.tencent.mm.ui.LauncherUI" +#手机ip +mobileIP = "192.168.0.102" +#重启周期 秒 +runningTime = 3600 + +adb="adb" + +while True: + restartApp(appPackageName, mobileIP, adb) + print("inject spider.js"); + print(jscode); + attach(appPackageName, mobileIP + ":27042", runningTime) + print("restarting spider.", appPackageName, mainActivity, mobileIP, runningTime) + + \ No newline at end of file diff --git a/traceJNI b/traceJNI new file mode 100755 index 0000000..2d119fd --- /dev/null +++ b/traceJNI @@ -0,0 +1,4 @@ +#!/bin/bash + +#./traceJNI com.ss.android.ugc.aweme +frida-trace -U -i "Java_*" $1 \ No newline at end of file diff --git a/xinitdeploy.py b/xinitdeploy.py new file mode 100644 index 0000000..52c2763 --- /dev/null +++ b/xinitdeploy.py @@ -0,0 +1,172 @@ +import frida +import os +import io +import traceback +import base64 +import platform + +def withColor(string, fg, bg=49): + print("\33[0m\33[%d;%dm%s\33[0m" % (fg, bg, string)) + +Red = 1 +Green = 2 +Yellow = 3 +Blue = 4 +Magenta = 5 +Cyan = 6 +White = 7 + +def red(string): + return withColor(string, Red+30) # Red +def yellow(string): + return withColor(string, Yellow+30) # Yellow + +warn = red +info = yellow + +rpc_jscode = """ +function mkdirs(dirpath) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(dirpath); + if (!file.exists()) { + file.mkdirs(); + } +} + +function writeFileAsBase64Content(filepath, base64) { + try { + var FileUtilsClz = Java.use("android.os.FileUtils"); + var StringClz = Java.use('java.lang.String'); + var Base64Clz = Java.use("android.util.Base64"); + var ByteArrayInputStreamClz = Java.use("java.io.ByteArrayInputStream"); + var FileOutputStreamClz = Java.use("java.io.FileOutputStream"); + var FileClz = Java.use("java.io.File"); + var distFilepath = FileClz.$new(filepath); + mkdirs(distFilepath.getParent()); + var javaBase64String = StringClz.$new(base64); + var getBytesMehtod = StringClz.getBytes.overload('java.lang.String'); + var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8'); + var decodeMethod = Base64Clz.decode.overload('[B', 'int'); + var originalBinary = decodeMethod.call(Base64Clz, bytes, 0); + var bais = ByteArrayInputStreamClz.$new(originalBinary); + if (FileUtilsClz.copy) { + var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream'); + var fos = FileOutputStreamClz.$new(distFilepath); + copyMehtod.call(FileUtilsClz, bais, fos); + } else if (FileUtilsClz.copyToFile) { + var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File'); + copyMehtod.call(FileUtilsClz, bais, distFilepath); + } + } catch(err) { + console.warn(err); + } +}; + +function checkFile(filepath, checkLength) { + var FileClz = Java.use("java.io.File"); + var file = FileClz.$new(filepath); + return file.exists() && file.length() == checkLength; +}; + +rpc.exports = { + write: function(filename, contentAsBase64) { + Java.perform(function() { + //console.log(contentAsBase64); + writeFileAsBase64Content(filename, contentAsBase64); + }); + }, + checkfile: function(filename, filelength) { + var ret = false; + Java.perform(function() { + ret = checkFile(filename, filelength); + }); + return ret; + }, +}; + +""" + +def getHookerDriverHost(): + try: + hookerDriver = io.open('../.hooker_driver', 'r', encoding= 'utf8').read() + hookerDriver = hookerDriver.replace("-H", "").strip() + return hookerDriver + except Exception: + return "-U" + +class XinitFile: + def __init__(self, filename, filepath): + self.filename = filename + self.filepath = filepath + + def fileData(self): + fopen = open(self.filepath,"rb") + data = fopen.read() + fopen.close() + return data + +def readXinitFiles(): + xinitFiles = [] + if not os.path.exists("./xinit"): + return xinitFiles + allFiles = os.listdir("./xinit") + for filename in allFiles: + filepath = "./xinit/" + filename + if os.path.isdir(filepath): + continue + xinitFile = XinitFile(filename, filepath) + xinitFiles.append(xinitFile) + return xinitFiles + +def on_message(message, data): + pass + +def attach(packageName): + online_session = None + online_script = None + hookerDriver = getHookerDriverHost() + try: + if hookerDriver != "-U": + rdev = frida.get_device_manager().add_remote_device(hookerDriver) + elif platform.system().find("Windows") != -1: + warn("推荐使用linux或mac操作系统获得更好的兼容性.") + rdev = frida.get_remote_device() + else: + rdev = frida.get_usb_device(1000) + online_session = rdev.attach(packageName) + if online_session == None: + warn("attaching fail to " + packageName) + online_script = online_session.create_script(rpc_jscode) + online_script.on('message', on_message) + online_script.load() + except Exception: + warn(traceback.format_exc()) + return online_session,online_script + + +def detach(online_session): + if online_session != None: + online_session.detach() + + +def xinitDeploy(packageName): + xinitFiles = readXinitFiles() + if xinitFiles == None or len(xinitFiles) == 0: + warn("deploying aborted. because not found any file in the path xinit.") + return + online_session = None + online_script = None + try: + online_session,online_script = attach(packageName); + for xinitFile in xinitFiles: + dataBase64 = base64.b64encode(xinitFile.fileData()).decode() + distPath = "/data/user/0/" + packageName + "/xinit/" + xinitFile.filename + info("copying " + xinitFile.filename + " to that path " + distPath + ".") + online_script.exports.write(distPath, dataBase64) + info("deploying xinit finished.") + except Exception: + warn("deploying xinit failure.") + warn(traceback.format_exc()) + finally: + detach(online_session) +