Repository: CreditTone/radar-frida
Branch: master
Commit: fd12f37e6597
Files: 58
Total size: 72.7 MB
Directory structure:
gitextract_c1gzjbhu/
├── .gitignore
├── .hooker_driver
├── README.md
├── README_EN.md
├── hooker.py
├── js/
│ ├── _hook_js_enhance.js
│ ├── _hook_js_prepare.js
│ ├── _hook_js_warp.js
│ ├── activity_events.js
│ ├── android_ui.js
│ ├── apk_shell_scanner.js
│ ├── bypass_frida_svc_detect.js
│ ├── bypass_root_detect.js
│ ├── bypass_vpn_detect.js
│ ├── cipher.js
│ ├── click.js
│ ├── dump_dex.js
│ ├── dump_so.js
│ ├── edit_text.js
│ ├── file.js
│ ├── find_anit_frida_so.js
│ ├── find_boringssl_custom_verify_func.js
│ ├── get_device_info.js
│ ├── hook_artmethod_register.js
│ ├── hook_encryption_algo.js
│ ├── hook_encryption_algo2.js
│ ├── hook_jni_method_trace.js
│ ├── hook_proxy_check.js
│ ├── hook_register_natives.js
│ ├── just_trust_me.js
│ ├── just_trust_me_for_ios.js
│ ├── keystore_dump.js
│ ├── object_store.js
│ ├── param_hook.js
│ ├── r0capture.js
│ ├── replace_dlsym_get_pthread_create.js
│ ├── rpc.js
│ ├── spoof_signature.js
│ ├── ssl_log.js
│ ├── text_view.js
│ ├── trace_init_proc.js
│ ├── url.js
│ └── webview_enable_debug.js
├── mobile-deploy/
│ ├── busybox-armv7m
│ ├── busybox-i686
│ ├── daemon_app.sh
│ ├── frida-server-16.7.19-android-arm
│ ├── frida-server-16.7.19-android-arm64
│ ├── frpc_arm
│ ├── frpc_arm64
│ ├── frpc_x86
│ ├── radar.dex
│ ├── redsocks
│ ├── redsocks.conf
│ ├── tcpforward_linux_arm
│ ├── tcpforward_linux_arm64
│ └── tcpforward_linux_x86
└── requirements.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.iml
*injectXServer.js
*spider.js
com.guazi*
com.ganji*
*.python-version
*/xinit/*.dex
*pydevproject
.DS_Store
log
.*source/.*
com.example.myapplication/
__handlers__/
build/
__pycache__
*.jar
.idea/
.classpath
.project
.settings/
target/
logs/
data/doc/
data/test/
.vscode/
com.*
cn.com.*
================================================
FILE: .hooker_driver
================================================
-U
================================================
FILE: README.md
================================================
[](https://mseep.ai/app/credittone-hooker)
免责声明
本项目的所有内容仅供学习与技术交流使用,旨在帮助开发者理解移动应用的结构和工作原理。
本项目不包含任何针对特定应用的破解操作或侵权内容。
针对某些app存在的简单解包技术内容在各大技术论坛网站均大量存在,并无对这些app造成实际损害
本项目无意协助任何非法用途,包括但不限于绕过版权保护、修改应用功能或获取未经授权的数据。
请确保在使用本项目工具时遵守相关法律法规,并仅用于个人学习或研究目的。
简体中文 | English
# 欢迎使用hooker逆向工作台





hooker是一个基于frida实现的逆向工具包。旨在为安卓逆向开发人员提供一个舒适的命令行界面和一些常用的通杀脚本、自动化生成hook脚本、内存漫游探测activity和service、frida版JustTrustMe、boringssl unpinning全网app通杀
为什么你需要hooker?
=================
* [1. frida版JustTrustMe,通杀全网APP,且作者一直在持续维护升级](#12-frida版JustTrustMe包括boringgssl)
* [2. 嵌入式webserver支持把App内部能力快速暴露成HTTP接口,便于做自动化和接口化](#7-嵌入式webserver)
* [3. 快捷设置socks5代理,无需额外安装socksdroid等三方app实现无感知代理](#11-快捷设置socks5无感代理)
---
目录
=================
* [快速开始](#快速开始)
* [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)
* [hook_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),然后跳回到这里
### 1. git clone项目
```shell
stephen@Mac:~$ git clone https://github.com/CreditTone/hooker.git
stephen@Mac:~$ cd hooker
```
### 2. 安装python3依赖
```shell
stephen@Mac:~/hooker$ pip3 install -r requirements.txt
```
### 3. root手机usb连接PC
```shell
stephen@Mac:~/hooker$ adb devices
List of devices attached
FA77C0301476 device
```
### 4. 启动hooker
这里注意,不要用绝对路径去执行,一定要cd到hooker目录下执行python3 hooker.py
hooker启动后将收集所有可调试app的信息,字段含义如下
- PID:当前app的主进程id,如果app没有启动则为0
- APP:app的名称
- IDENTIFIER:app的包名
- EXIST_REVERSE_DIRECTORY:如果app曾经被调试过就是✅,从没被调试过就是❌
```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):
```
***
### 5. 输入调试应用包名
- 输入调试应用包名回车后,如果是第一次调试应用,hooker将创建应用目录,应用目录名称为应用的Identifier,用于存放所有js脚本和快捷命令。
- hooker将帮你检测当前app是否启动且在手机前台,如不在启动帮你启动,如不在前台帮你切到前台
- frida通杀脚本可以在hooker交互式命令行下用attach/spawn执行,也可以手动cd到应用目录用快捷命令或原生的frida命令执行。
- 你可以修改应用工作目录下任何脚本
```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
hook_jni_method_trace.js url.js just_trust_me_okhttp_hook_finder_for_android.js
text_view.js find_anit_frida_so.js
某皮 >
```

***
### 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
某皮 >
```

***
### 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,并把生成结果放到目标应用的工作目录中。
***
### 8. 自动化生成frida脚本
自动化生成脚本是hooker的杀器。虽然现在AI大模型也可以写,但是我们离内存近,更快,也不需要联网。生成的脚本自带打印堆栈等信息,和一些你可能需要的扩展方法。
另外在生成脚本的过程中,命令行类名、方法名提示也可以当作搜索使用,能通过关键词快速搜索定位类方法。hooker搜索类比jadx快很多,不信就试试......

- Command语法:gs, generatescript [class_name:method_name]
- 8.1 生成指定方法的frida hook脚本:
gs okhttp3.Request$Builder:addHeader,参数部分(String, String)不是必须写的
```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 生成指定类的所有成员方法的frida hook脚本:
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 生成指定类的构造方法的frida hook脚本:
gs okhttp3.Request$Builder:_ 或者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. 列出应用目录所有frida脚本
- 查看应用目录下所有的脚本,这里有hooker给你生成的通杀脚本,也有您生成的指定hook脚本,您可以修改定制。
- 如果您有自定义的脚本放到应用目录下,ls命令可以将脚本名称刷入命令行提示缓存
```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 hook_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执行frida脚本
在hooker下执行frida脚本,您只需要输入attach【空格】脚本名称会自动弹出提示。上下选择需要的脚本按tab键即可自动输入全名称。
这是hooker在追求极致的工匠精神,帮助您从开波音737到开空客320的跳跃。
> **日志同步**:`attach` / `frida` 执行脚本时,Frida 脚本的 `send()` 输出会自动同步保存到应用工作目录下的同名 `.log` 文件。例如 `attach url.js` 会生成 `url.log`,方便喂给 AI 分析。
```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. 快捷设置socks5无感代理
通过iptables链路层转发包实现一键设置代理,优势是APP完全无感知被代理。推荐使用charles的socks5
设置代理后必须主动去[关闭代理](#14-取消代理设置),代理不会自动取消
```shell
某音 > proxy socks5://10.112.99.11:9998
proxy socks5://10.112.99.11:9998 OK
某音 >
```
***
### 12. frida版JustTrustMe(包括boringgssl)
关掉SSL证书校验
```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执行frida脚本
> **日志同步**:`spawn` / `fridaf` 执行脚本时,Frida 脚本的 `send()` 输出同样会自动同步保存到同名 `.log` 文件。例如 `spawn just_trust_me.js` 会生成 `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. 取消代理设置
一键取消代理
```shell
某音 > unproxy
unproxy OK
某音 >
```
***
### 15. 重启app
```shell
某信拍 > restart
restarts com.xxx.buyxxphone
```
***
### 16. 获取uid和pid
```shell
某信拍 > uid
10189
某信拍 > pid
3509
```
***
### 17. pull文件
快捷拉取文件到本地应用工作目录
```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
```
***
### 18. r0capture
hooker集成了r0capture,抓包产生的pcap文件保存在{应用包名}/r0capture_ssl.pcap路径下,如酷安:com.coolapk.market/r0capture_ssl.pcap
```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
```
***
### 19. upgrade
hooker更新频繁,平均周更约10次。upgrade帮助您随时同步最新代码和相关文件到本地。
```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

***
### 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

***
### activity_events.js
当你需要跟踪start某个Activity启动时可执行,获取startActivity的intent信息和调用堆栈。
推荐命令:frida activity_events.js

***
### 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




***
### edit_text.js
用于跟踪获取 EditText 的 `getText()` 事件,并获取其真实 Class 类型。
EditText 通常绑定搜索按钮或输入事件,是定位“搜索”接口实现代码的有效入口,辅助识别核心业务逻辑。
推荐命令:frida edit_text.js

***
### hook_register_natives.js
用于拦截 Android JNI RegisterNatives 函数的 Frida 脚本,主要用于分析和修改原生方法(native methods)的注册过程
推荐命令:spawn hook_register_natives.js

***
### text_view.js
用于跟踪 TextView 的 `setText()` 和 `getText()` 调用,并输出其真实 Class。
常用于提取页面展示的明文数据,以及获取堆栈调用信息,从而追踪业务层的 model 构造逻辑和数据源。
推荐命令:spawn/attach text_view.js

---
### activity_events.js
用于跟踪 Android Activity 生命周期(如 `onCreate`、`onResume`)的 Frida 脚本,帮助分析 Activity 初始化逻辑。
推荐命令:spawn/attach activity_events.js
---
### ssl_log.js
用于在 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
用于 trace `init_proc` 函数调用流程的脚本。
需手动指定 `startAddr`、`endAddr` 和模块名 `somodule`,适合分析 native 启动流程。
推荐命令:spawn/attach trace_init_proc.js

---
### hook_artmethod_register.js
用于拦截 Android ART 虚拟机中 `ArtMethod` 的注册函数,适合深入分析虚拟机行为与 native 方法绑定。
推荐命令:spawn/attach hook_artmethod_register.js
---
### find_anit_frida_so.js
用于发现 app 中加载的可疑 anti-frida 动态库,识别顺序为:**谁最后加载、谁让 app 崩溃,谁就是反调试的嫌疑人**。
推荐命令:spawn/attach find_anit_frida_so.js

---
### hook_jni_method_trace.js
用于追踪 Native 层回调 Java 方法的行为,可观察 so 层与 Java 的交互,帮助分析 JNI 层调用栈。
推荐命令:spawn/attach hook_jni_method_trace.js
---
### replace_dlsym_get_pthread_create.js
专门对抗 `libmsaoaidsec.so` 中使用 `dlsym` 获取 `pthread_create` 的反调试方式。
本脚本 hook `dlsym`,用于劫持线程创建行为,从而对抗动态加载的反调试代码。
推荐命令:spawn/attach replace_dlsym_get_pthread_create.js

---
### find_boringssl_custom_verify_func.js
用于查找 `boringssl` 中注册的自定义证书验证函数,通过 hook `SSL_CTX_set_custom_verify` 定位目标函数,并实现强制信任(返回 0)。
**使用建议:**
执行前请清除目标 app(如某音)的缓存,以保证函数重新注册。
推荐命令:spawn find_boringssl_custom_verify_func.js



---
### get_device_info.js
用于全面获取设备指纹信息,包括:
- Android ID、IMEI、指纹、厂商、系统信息
- 安装的所有应用(含系统 app)
- 传感器信息(如名称、厂商、延迟)
- 系统状态(是否 root、运行时、内核信息等)
推荐命令:attach get_device_info.js

提供以下 4 个调用方法:
- `getBasicInfo()`:基础信息
- `getInstalledPackages()`:应用列表
- `getSensos()`:传感器信息
- `getSystemInfo()`:系统与运行环境

---
### apk_shell_scanner.js
动态识别 APK 加壳技术的脚本,支持多种主流壳,如娜迦、爱加密、360、梆梆、腾讯御、网易易盾等。
推荐命令:attach apk_shell_scanner.js
识别结果示例:
- This app is protected by {爱加密}
- This app is not protected or uses an unknown protection scheme
---
### bypass_frida_svc_detect.js
绕过 app 对 Frida Server 的检测逻辑,适用于反调试保护较强的目标应用。
推荐命令:spawn/attach bypass_frida_svc_detect.js
---
### bypass_root_detect.js
用于绕过 root 检测逻辑,使得 root 环境下也能正常运行 app。
推荐命令:spawn/attach bypass_root_detect.js
---
### bypass_vpn_detect.js
用于绕过 VPN 检测逻辑,避免 app 阻止使用代理、VPN 或抓包工具。
推荐命令:spawn/attach bypass_vpn_detect.js
---
### hook_encryption_algo.js
用于 hook 典型加密算法(如 AES、RSA、HMAC 等)的实现函数,分析加密参数、明文与密文数据。
推荐命令:spawn/attach hook_encryption_algo.js
---
### hook_encryption_algo2.js
拓展版本的加密算法 hook 脚本,适用于更复杂的加密场景或多路加密调用链。
推荐命令:spawn/attach hook_encryption_algo2.js
### webview_enable_debug.js
该脚本用于 Hook Android 应用中的 WebView 行为,特别是在 WebView 初始化和执行 JavaScript 时进行监控和调试。适用于分析 WebView 加载的页面、动态注入的 JavaScript 代码,以及定位 JS 调用位置,常用于逆向分析、抓取数据或识别加固方案中的 Web 容器。
推荐命令:spawn webview_enable_debug.js
# Windows安装WSL
WSL是适用于Linux 的Windows 子系统(WSL)允许开发人员直接在Windows 上运行GNU/Linux 环境(包括大多数命令行工具、实用工具和应用程序),无需传统虚拟机或双启动设置的开销。
### 1. 安装wsl ubuntn24.04
访问:https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#downloading-distributions
下载Ubuntu24.04
双击Ubuntu2404-240425.AppxBundle 安装Ubuntu
### 2. 进入wsl,配置代理
窗口输入wsl进入ubuntu命令行
- cmd
- wsl
切换到root用户
- sudo su
配置翻墙代理,如果你有
- export http_proxy="http://10.115.164.50:8080"
- export https_proxy="http://10.115.164.50:8080"
### 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
# 自定义frida-server
- 将您自定义的frida-server文件拷贝到mobile-deploy文件夹下
- 修改hooker.py,default_frida_server_arm和default_frida_server_arm64变量的名字为你自定义的文件名
```python
default_frida_server_arm = "your-custom-frida-server-android-arm"
default_frida_server_arm64 = "your-custom-frida-server-android-arm64"
```
# hooker命令行快捷键
- Ctrl + U:整行清空
- Ctrl + W:删除一个单词
- Ctrl + K:从光标删到行尾
================================================
FILE: README_EN.md
================================================
⚠️ 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





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:

- 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
hook_jni_method_trace.js url.js just_trust_me_okhttp_hook_finder_for_android.js
text_view.js find_anit_frida_so.js
某皮 >
```

***
### 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
某皮 >
```

***
### 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

- 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 hook_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
```
***
================================================
FILE: hooker.py
================================================
#!/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 base64
import time
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
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')
# 分别收集 d8 和 dx 文件
if os.path.isfile(d8_path) and os.access(d8_path, os.X_OK):
d8_files.append((item, d8_path))
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:
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()
#初始化frida运行环境
def is_frida_working_via_attach(target_package="com.android.systemui"):
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()
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
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():
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:
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 ""
try:
# 56cf2745f4884b4dfcc1e193d0118c05 radar.dex
m = re.search(r"[\w]{32}", result)
if m:
return m.group()
else:
return ""
except Exception:
return ""
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")
compara_and_update_file("mobile-deploy/libext64.so", f"/data/data/{current_identifier}/files/libext64.so")
compara_and_update_file("mobile-deploy/libext.so", f"/data/data/{current_identifier}/files/libext.so")
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]
filename = remote_file.split("/")[-1]
#print("filename:", filename)
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)
#print(f"local_md5:{local_md5} remote_md5:{remote_md5}")
if local_md5 != remote_md5:
#info(f"update file {local_filename} to {remote_file}")
run_su_command(f"cp /sdcard/{local_filename} {remote_file}", True)
run_su_command(f"chmod 555 {remote_file}", 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
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
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
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
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):
online_session = None
online_script = None
try:
online_session, online_script,_ = attach_rpc(target);
info(online_script.exports_sync.containsclass(className))
except Exception:
warn(traceback.format_exc())
finally:
detach(online_session, online_script)
def create_workingdir_file(filename, text):
file = None
try:
file = open(filename, mode='w+')
file.write(text)
except Exception:
warn(traceback.format_exc())
finally:
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
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")
os.popen('chmod 777 ' + packageName +'/hooking').readlines()
os.popen('chmod 777 ' + packageName +'/attach').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",
"hook_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):
online_session = None
online_script = None
packageName = current_identifier
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
if savePath == None:
defaultFilename = className.replace(":", ".").replace("$", ".").replace("__", ".")+ "." + file_method_name + ".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"
warpExtraInfo += ganaretoionJscode
create_workingdir_file(savePath, jsbeautifier.beautify(warpExtraInfo))
info("frida hook script: " + defaultFilename)
else:
warn("Not found any classes by pattern "+hookCmdArg+".")
except Exception:
warn(traceback.format_exc())
finally:
detach(online_session, online_script)
def print_activitys():
online_session = None
online_script = None
try:
online_session,online_script = attach_rpc();
info(online_script.exports_sync.activitys())
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
def print_services():
online_session = None
online_script = None
try:
online_session, online_script = attach_rpc();
info(online_script.exports_sync.services())
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
def print_object(objectId):
online_session = None
online_script = None
try:
online_session, online_script = attach_rpc();
info(online_script.exports_sync.objectinfo(objectId))
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
def object_to_explain(objectId):
online_session = None
online_script = None
try:
online_session, online_script = attach_rpc();
info(online_script.exports_sync.objecttoexplain(objectId))
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
def print_view(viewId):
online_session = None
online_script = None
try:
online_session, online_script = attach_rpc();
report = online_script.exports_sync.viewinfo(viewId)
info(report);
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
def rpc_start_web_server(dex_file, all_class):
global webserver_url
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)
except Exception:
print(traceback.format_exc())
finally:
detach(online_session, online_script)
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
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
except Exception:
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')
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}"
compara_and_update_file(f"{current_identifier}/{local_file}", 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)
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...")
finally:
p.terminate() # 结束 tail 进程
try:
p.wait(timeout=2)
except subprocess.TimeoutExpired:
p.kill()
print("viewlog stopped.")
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 len(sys.argv) > 1:
arg = sys.argv[1]
if arg == "upgrade":
upgrade()
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):
sys.exit(2);
================================================
FILE: js/_hook_js_enhance.js
================================================
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
}
================================================
FILE: js/_hook_js_prepare.js
================================================
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);
};
================================================
FILE: js/_hook_js_warp.js
================================================
rpc.exports = {
cleanup: function () {
// 清理所有拦截器
Interceptor.detachAll();
// 如果你设置了定时器或 Stalker,也可以清理
Stalker.unfollow();
// clearInterval(...);
}
};
================================================
FILE: js/activity_events.js
================================================
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/local/tmp/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);
};
});
================================================
FILE: js/android_ui.js
================================================
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/local/tmp/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());
});
}
================================================
FILE: js/apk_shell_scanner.js
================================================
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.');});
================================================
FILE: js/bypass_frida_svc_detect.js
================================================
# 自实现系统函数一个重要的前提就是它们都有标准的系统调用号,标准的机器码。所以我们绕过的时候也可以用同样的思路。
# 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();
================================================
FILE: js/bypass_root_detect.js
================================================
/*
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;
}
}
});
================================================
FILE: js/bypass_vpn_detect.js
================================================
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)
================================================
FILE: js/cipher.js
================================================
//javax.crypto.Cipher:?
function classExists(className) {
var exists = false;
try {
var clz = Java.use(className);
exists = true;
} catch(err) {
//console.log(err);
}
return exists;
};
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);
};
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) {
var startTime = new Date().getTime();
java_security_SecureRandom_clz_method_setSeed_tsea.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public void java.security.SecureRandom.setSeed(long)', executor);
};
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) {
var startTime = new Date().getTime();
java_security_SecureRandom_clz_method_setSeed_c9w7.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public synchronized void java.security.SecureRandom.setSeed(byte[])', executor);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_std2.call(this, v0, v1);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_chy9.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
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 startTime = new Date().getTime();
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 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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_da7l.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_update_ok86.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[],int,int)', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final javax.crypto.ExemptionMechanism javax.crypto.Cipher.getExemptionMechanism()', executor);
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 startTime = new Date().getTime();
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 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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_uuhl.call(this, v0, v1);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.cert.Certificate) throws java.security.InvalidKeyException', executor);
};
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 startTime = new Date().getTime();
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 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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getOutputSize_f23n.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getOutputSize(int)', executor);
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 startTime = new Date().getTime();
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 executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[],int) throws javax.crypto.ShortBufferException', executor);
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 startTime = new Date().getTime();
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 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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_34ei.call(this, v0, v1, v2, v3);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
var javax_crypto_Cipher_clz_method_getAlgorithm_lyxb = javax_crypto_Cipher_clz.getAlgorithm.overload();
javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.implementation = function() {
var startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final java.lang.String javax.crypto.Cipher.getAlgorithm()', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_invalidTransformation_ntcg.call(javax_crypto_Cipher_clz, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
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 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);
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 startTime = new Date().getTime();
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 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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed.call(javax_crypto_Cipher_clz, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = 'javax_crypto_Cipher_clz';
methodInBeat(invokeId, startTime, 'private static void javax.crypto.Cipher.checkInputOffsetAndCount(int,int,int)', executor);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_update_mmvk.call(this, v0, v1);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getProvider_u7pr.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final java.security.Provider javax.crypto.Cipher.getProvider()', executor);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_updateAAD_alep.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[],int,int)', executor);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh.call(javax_crypto_Cipher_clz, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_update_bto4.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[])', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_dxgo.call(this, v0, v1, v2, v3);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getInstance_03ug.call(javax_crypto_Cipher_clz, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_u19e.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_updateAAD_ojmm.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(java.nio.ByteBuffer)', executor);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_ixw8.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getBlockSize_wjca.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getBlockSize()', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_wrap_05i9.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m.call(javax_crypto_Cipher_clz, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_checkTransformation_qoag.call(javax_crypto_Cipher_clz, v0);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getParameters_6auy.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final java.security.AlgorithmParameters javax.crypto.Cipher.getParameters()', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getSpi_963r.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi(java.security.Key)', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_k1n2.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal(byte[]) throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getSpi_vlc2.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi()', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_getIV_2b1b.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.getIV()', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_unwrap_azqe.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_l66q.call(this, v0, v1);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key) throws java.security.InvalidKeyException', executor);
};
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_w1fs.call(this, v0, v1, v2, v3);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
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 startTime = new Date().getTime();
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 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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_checkMode_8g2b.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'private void javax.crypto.Cipher.checkMode(int)', executor);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_update_ll68.call(this, v0, v1, v2, v3);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[]) throws javax.crypto.ShortBufferException', executor);
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_62jl.call(this);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal() throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_init_c9qo.call(this, v0, v1, v2);
var invokeId = Math.random().toString(36).slice( - 8);
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);
};
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 startTime = new Date().getTime();
var ret = javax_crypto_Cipher_clz_method_doFinal_rso8.call(this, v0, v1);
var invokeId = Math.random().toString(36).slice( - 8);
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);
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) {
var startTime = new Date().getTime();
javax_crypto_Cipher_clz_method_updateAAD_ry91.call(this, v0);
var invokeId = Math.random().toString(36).slice( - 8);
var executor = this.hashCode();
methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[])', executor);
};
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 startTime = new Date().getTime();
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 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);
return ret;
};
});
================================================
FILE: js/click.js
================================================
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;
};
});
================================================
FILE: js/dump_dex.js
================================================
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);
================================================
FILE: js/dump_so.js
================================================
function dump_so(so_name) {
if (Java.available) {
Java.perform(function () {
var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
var libso = Process.getModuleByName(so_name);
console.log("[name]:", libso.name);
console.log("[base]:", libso.base);
console.log("[size]:", ptr(libso.size));
console.log("[path]:", libso.path);
var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
var file_handle = new File(file_path, "wb");
if (file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size, 'rwx');
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.log("[dump]:", file_path);
}
});
}
}
================================================
FILE: js/edit_text.js
================================================
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;
};
}
});
================================================
FILE: js/file.js
================================================
function writeFileAsBase64Content(filepath, base64) {
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 FileUtilsClz = Java.use("android.os.FileUtils");
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);
var fos = FileOutputStreamClz.$new(FileClz.$new(filepath));
var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream');
copyMehtod.call(FileUtilsClz, bais, fos);
};
function fileExists(filepath) {
var FileClz = Java.use("java.io.File");
return FileClz.$new(filepath).exists();
};
rpc.exports = {
write: function(filename, contentAsBase64) {
Java.perform(function() {
writeFileAsBase64Content(filename, contentAsBase64);
});
},
exists: function(filename) {
var ret = false;
Java.perform(function() {
ret = fileExists(filename);
});
return ret;
},
};
================================================
FILE: js/find_anit_frida_so.js
================================================
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()
================================================
FILE: js/find_boringssl_custom_verify_func.js
================================================
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()
================================================
FILE: js/get_device_info.js
================================================
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']();}});}
================================================
FILE: js/hook_artmethod_register.js
================================================
function hook_ArtMethodRegister() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var ArtMethodRegisterNative = null;
var ArtMethod_PrettyMethod = null;
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
var address = symbol.address;
var name = symbol.name;
var indexArtMethod = name.indexOf("ArtMethod");
//_ZN3art9ArtMethod14RegisterNativeEPKv android 10
if (
name.indexOf("ArtMethod") >= 0 &&
name.indexOf("RegisterNative") >= 0 &&
name.indexOf("Callback") < 0
) {
console.log("ArtMethod::RegisterNative is at ", address, name);
ArtMethodRegisterNative = address;
}
if (indexArtMethod >= 0 && name.indexOf("PrettyMethod") >= 0 && name.indexOf("Eb") >= 0) {
console.log("ArtMethod::PrettyMethod is at ", address, name);
ArtMethod_PrettyMethod = address;
}
}
var module_libext = null;
if (Process.arch === "arm64") {
module_libext = Module.load("/data/data/com.smile.gifmaker/files/libext64.so");
} else if (Process.arch === "arm") {
module_libext = Module.load("/data/data/com.smile.gifmaker/files/libext.so");
}
if (module_libext != null) {
var addr_PrettyMethod = module_libext.findExportByName("PrettyMethod");
var PrettyMethod = new NativeFunction(addr_PrettyMethod, "void", ["pointer", "pointer", "pointer", "int"]);
if (ArtMethodRegisterNative) {
//var foo_ArtMethod_PrettyMethod = new NativeFunction(ArtMethod_PrettyMethod, "pointer", ["pointer", "int"]);
Interceptor.attach(ArtMethodRegisterNative, {
onEnter: function (args) {
try {
var result = Memory.alloc(0x100);
var fnPtr_ptr = args[1];
var find_module = Process.findModuleByAddress(fnPtr_ptr);
var offset = ptr(fnPtr_ptr).sub(find_module.base)
PrettyMethod(ArtMethod_PrettyMethod, args[0], result, 0x100);
console.log("[ArtMethod_RegisterNative] Method_sig:", result.readCString(), "module_name:", find_module.name, "offset:", offset);
} catch (error) {
console.log(error);
}
}, onLeave: function (retval) {
}
});
}
}
}
setImmediate(hook_ArtMethodRegister)
================================================
FILE: js/hook_encryption_algo.js
================================================
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;
}
});
================================================
FILE: js/hook_encryption_algo2.js
================================================
//打印堆栈
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;
}
================================================
FILE: js/hook_jni_method_trace.js
================================================
function hook_libart() {
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) {
var env = args[0]; // JNIEnv*
var jclass = args[1]; // Java 类
var jmethodID = args[2]; // 方法 ID
console.log("addrCallStaticObjectMethod [*] CallStaticObjectMethod Hooked!");
console.log("addrCallStaticObjectMethod 📌 JNIEnv*: " + env);
console.log("addrCallStaticObjectMethod 📌 JClass: " + jclass);
console.log(" addrCallStaticObjectMethod 📌 JMethodID: " + jmethodID);
// 试图解析方法名(需要配合 GetMethodName )
try {
var methodName = Java.vm.getEnv().getMethodName(jmethodID);
console.log("addrCallStaticObjectMethod Method Name: " + methodName);
} catch (err) {
console.log("addrCallStaticObjectMethod Failed to get method name");
}
},
onLeave: function (retval) {
console.log("addrCallStaticObjectMethod 📤 Return Value: " + retval);
}
});
} else {
console.log("[-] CallStaticObjectMethod symbol not found!");
}
if (addrCallVoidMethod) {
Interceptor.attach(ptr(addrCallVoidMethod), {
onEnter: function (args) {
console.log("addrCallVoidMethod [+] Hooked CallVoidMethod");
console.log("addrCallVoidMethod 📌 JNIEnv*: " + args[0]);
console.log("addrCallVoidMethod 📌 jobject: " + args[1]);
console.log("addrCallVoidMethod 📌 jmethodID: " + args[2]);
var jclass = Java.cast(args[1], Java.use('java.lang.Object')).getClass();
console.log("addrCallVoidMethod 📌 Java Class: " + jclass);
},
onLeave: function (retval) {
console.log("addrCallVoidMethod 📤 Return Value: " + retval);
}
});
} else {
console.log("[-] CallVoidMethod symbol not found!");
}
if (addrCallObjectMethod != null) {
Interceptor.attach(addrCallObjectMethod, {
onEnter: function (args) {
console.log("[addrCallObjectMethod] called:");
// 获取 JNIEnv
this.jni_env = args[0];
console.log("addrCallObjectMethod 📌 JNIEnv: " + this.jni_env);
// 获取调用的 Java 对象 (jobject)
this.jobject = args[1];
console.log("addrCallObjectMethod 📌 jobject: " + this.jobject);
// 获取方法 ID (jmethodID)
this.jmethodID = args[2];
console.log("addrCallObjectMethod 📌 jmethodID: " + this.jmethodID);
// 获取 Java 方法参数
this.args_ptr = args[3]; // 可能是变长参数 (depends on overload)
console.log("addrCallObjectMethod 📌 args_ptr: " + this.args_ptr);
// 获取调用的 Java 方法名
var jclass = Java.cast(this.jobject, Java.use('java.lang.Object')).getClass();
console.log("addrCallObjectMethod 📌 Java Class: " + jclass);
},
onLeave: function (retval) {
if (retval != null) {
// var bytes = Memory.readCString(retval);
// console.log("[GetStringUTFChars] result:" + bytes);
}
}
});
}
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) {
this.utf8_string = args[1].readUtf8String(); // 读取传入的 UTF-8 字符串
//console.log("[*] Hooked NewStringUTF");
if (this.utf8_string) {
console.log("addrNewStringUTF 📌 UTF-8 String: " + this.utf8_string);
// 获取当前线程的调用栈
var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);
// 获取调用该方法的地址(栈中的上一级)
var callerAddress = backtrace[1]; // backtrace[0] 是当前方法的地址,backtrace[1] 是调用者的地址
console.log("addrNewStringUTF [*] Caller Address: " + callerAddress);
var find_module = Process.findModuleByAddress(callerAddress);
if (find_module) {
console.log("addrNewStringUTF |--> Module: " + find_module.name + " offset:" + callerAddress.sub(find_module.base));
}
}
},
onLeave: function (retval) {
//console.log(" |--> Method ID: " + retval);
}
});
}
if (addrFindClass != null) {
Interceptor.attach(addrFindClass, {
onEnter: function (args) {
if (args[1] != null) {
var stackTraceMsg = Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n');
var name = Memory.readCString(args[1]);
console.log("addrFindClass name:" + name + " stackTraceMsg:" + stackTraceMsg);
}
},
onLeave: function (retval) {}
});
}
if (addrGetMethodID != null) {
Interceptor.attach(ptr(addrGetMethodID), {
onEnter: function (args) {
this.env = args[0]; // JNIEnv
this.jclass = args[1]; // jclass
this.method_name = args[2].readCString(); // Method name
this.method_sig = args[3].readCString(); // Method signature
console.log("addrGetMethodID |--> Method Name: " + this.method_name);
if (this.method_name.indexOf("sendRequest") != -1) {
// 获取当前线程的调用栈
var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);
// 获取调用该方法的地址(栈中的上一级)
var callerAddress = backtrace[1]; // backtrace[0] 是当前方法的地址,backtrace[1] 是调用者的地址
console.log("addrGetMethodID [*] Caller Address: " + callerAddress);
var find_module = Process.findModuleByAddress(callerAddress);
console.log("\naddrGetMethodID [*] Hooked GetMethodID");
console.log("addrGetMethodID |--> Method Name: " + this.method_name);
console.log("addrGetMethodID |--> Method Signature: " + this.method_sig);
console.log("addrGetMethodID |--> Module: " + find_module.name + " offset:" + callerAddress.sub(find_module.base));
}
},
onLeave: function (retval) {
//console.log(" |--> Method ID: " + retval);
}
});
}
if (addrGetStaticMethodID != null) {
Interceptor.attach(addrGetStaticMethodID, {
onEnter: function (args) {
if (args[2] != null) {
this.method_name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[addrGetStaticMethodID] name:" + this.method_name + ", sig:" + sig);
} else {
console.log("[addrGetStaticMethodID] name:" + this.method_name);
}
}
},
onLeave: function (retval) {}
});
}
if (addrGetFieldID != null) {
Interceptor.attach(addrGetFieldID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[addrGetFieldID] name:" + name + ", sig:" + sig);
} else {
console.log("[addrGetFieldID] name:" + name);
}
}
},
onLeave: function (retval) {}
});
}
if (addrGetStaticFieldID != null) {
Interceptor.attach(addrGetStaticFieldID, {
onEnter: function (args) {
if (args[2] != null) {
var name = Memory.readCString(args[2]);
if (args[3] != null) {
var sig = Memory.readCString(args[3]);
console.log("[addrGetStaticFieldID] name:" + name + ", sig:" + sig);
} else {
console.log("[addrGetStaticFieldID] name:" + name);
}
}
},
onLeave: function (retval) {}
});
}
if (addrRegisterNatives != null) {
Interceptor.attach(addrRegisterNatives, {
onEnter: function (args) {
console.log("[addrRegisterNatives] method_count:", args[3]);
var env = args[0];
var java_class = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(java_class);
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("[addrRegisterNatives] 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));
}
},
onLeave: function (retval) {}
});
}
}
setImmediate(hook_libart);
================================================
FILE: js/hook_proxy_check.js
================================================
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.');
});
================================================
FILE: js/hook_register_natives.js
================================================
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);
================================================
FILE: js/just_trust_me.js
================================================
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);
================================================
FILE: js/just_trust_me_for_ios.js
================================================
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.')
}
================================================
FILE: js/keystore_dump.js
================================================
//在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;
}
})
================================================
FILE: js/object_store.js
================================================
function loadDexfile(dexfile) {
Java.perform(function() {
Java.openClassFile(dexfile).load();
});
};
loadDexfile('/data/local/tmp/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);
};
================================================
FILE: js/param_hook.js
================================================
//crack by com.smile.gifmaker 7.5.40.14691
//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 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);
};
//虽然我们习惯用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) {}
};
var containRegExps = new Array(RegExp(/NStokensig/))
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;
}
}
return true;
}
Java.perform(function() {
var java_util_HashMap_clz = Java.use('java.util.HashMap');
var java_util_HashMap_clz_method_put_wh2m = java_util_HashMap_clz.put.overload('java.lang.Object', 'java.lang.Object');
java_util_HashMap_clz_method_put_wh2m.implementation = function(v0, v1) {
var invokeId = Math.random().toString(36).slice( - 8);
var startTime = new Date().getTime();
var executor = this.hashCode();
var ret = java_util_HashMap_clz_method_put_wh2m.call(this, v0, v1);
if (check(v0)) {
log(v0 + "=" + v1);
methodInBeat(invokeId, startTime, 'public java.lang.Object java.util.HashMap.put(java.lang.Object,java.lang.Object)', executor);
}
return ret;
};
var android_util_Pair_clz = Java.use('android.util.Pair');
var android_util_Pair_clz_init_74e4 = android_util_Pair_clz.$init.overload('java.lang.Object', 'java.lang.Object');
android_util_Pair_clz_init_74e4.implementation = function(v0, v1) {
var invokeId = Math.random().toString(36).slice( - 8);
var startTime = new Date().getTime();
var executor = this.hashCode();
var returnObj = android_util_Pair_clz_init_74e4.call(this, v0, v1);
var v0ClassName = getClassName(v0);
if (v0ClassName == "java.lang.String" && check(v0.toString())) {
log(v0 + "=" + v1);
methodInBeat(invokeId, startTime, 'public android.util.Pair(java.lang.Object,java.lang.Object)', executor);
}
return returnObj;
};
});
================================================
FILE: js/r0capture.js
================================================
/**
* 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(...);
}
};
================================================
FILE: js/replace_dlsym_get_pthread_create.js
================================================
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)
================================================
FILE: js/rpc.js
================================================
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;
};
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 = "";
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;
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();
}
});
return result;
},
loadradardex: function() {
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();
}
});
},
containsclass: function(className) {
var result = false;
Java.perform(function() {
result = class_exists(className);
});
return result;
},
hookjs: function(className, toSpace) {
var found = true;
var hookJs = "Java.perform(function() {\n";
var className = className;
var methodName = toSpace;
Java.perform(function() {
var radarClassResult = discoverClass(className);
if (radarClassResult) {
hookJs += generateMethodHookJs(radarClassResult, methodName);
hookJs += "});";
} else {
found = false;
console.error("Not found class " + className);
}
});
if (found) {
return hookJs;
}
return "";
},
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;
},
activitys: function() {
var report = "";
Java.perform(function() {
try {
var radarAndroidClz = Java.use("gz.radar.Android");
var activityInfos = radarAndroidClz.getActivityInfos();
report += ("Found Activities: " + activityInfos.length) + "\n";
for (var i = 0; i < activityInfos.length; i++) {
try {
report += ("------------------" + (i) + "--------------------") + "\n";
var activityInfo = activityInfos[i];
report += ("Activity Title: " + activityInfo.getTitle()) + "\n";
report += ("Activity Class: " + activityInfo.getClazz()) + "\n";
report += ("Activity SuperClass: " + activityInfo.getSuperClazz()) + "\n";
report += ("Activity ImplementInterfaces: " + activityInfo.getImplementInterfaces()) + "\n";
report += ("Activity OnTop: " + activityInfo.isOnTop()) + "\n";
report += ("Activity Paused: " + activityInfo.isPaused()) + "\n";
report += ("Activity Stopped: " + activityInfo.isStopped()) + "\n";
var androidApkFields = activityInfo.getAndroidApkFields();
report += ("Activity Fields: " + androidApkFields.length) + "\n";
for (var j = 0; j < androidApkFields.length; j++) {
report += ("\t" + androidApkFields[j].toLine()) + "\n";
}
var methods = activityInfo.methods();
report += ("Activity Methods: " + methods.length) + "\n";
for (var j = 0; j < methods.length; j++) {
report += ("\t" + methods[j]) + "\n";
}
} catch(err) {
console.log(err);
}
}
} catch(err) {
console.log(err);
}
});
return report;
},
services: function() {
var report = "";
Java.perform(function() {
var radarAndroidClz = Java.use("gz.radar.Android");
var serviceInfos = radarAndroidClz.getServiceInfos();
report += "Found Services: " + serviceInfos.length + "\n";
for (var i = 0; i < serviceInfos.length; i++) {
report += ("------------------" + (i) + "--------------------") + "\n";
var serviceInfo = serviceInfos[i];
report += ("Service Class: " + serviceInfo.getName()) + "\n";
report += ("Service SuperClass: " + serviceInfo.getSuperClazz()) + "\n";
report += ("Service ImplementInterfaces: " + serviceInfo.getImplementInterfaces()) + "\n";
var androidApkFields = serviceInfo.getAndroidApkFields();
report += ("Service Fields: " + androidApkFields.length) + "\n";
for (var j = 0; j < androidApkFields.length; j++) {
report += ("\t" + androidApkFields[j].toLine()) + "\n";
}
var methods = serviceInfo.methods();
report += ("Service Methods: " + methods.length) + "\n";
for (var j = 0; j < methods.length; j++) {
report += ("\t" + methods[j]) + "\n";
}
}
});
return report;
},
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";
}
});
}
return report;
},
objecttoexplain: function(objectId) {
var report = "";
Java.perform(function() {
var radarAndroidClz = Java.use("gz.radar.Android");
var explainObjs = radarAndroidClz.object2Explain(objectId);
if (explainObjs == null) {
report += "Not Found Any Object.";
return;
}
if (explainObjs.isEmpty()) {
report += "Cannot Explain the Object " + objectId+".";
return;
}
report += "Found Objects: " + explainObjs.size() + "\n";
for (var i = 0; i < explainObjs.size(); i++) {
var key = explainObjs.getKey(i);
var _objectId = explainObjs.getObjectId(i);
report += ("------------------[" + key + "]--------------------") + "\n";
report += ("Object Class: " + explainObjs.getObjectClass(i)) + "\n";
report += ("Object Id:" + _objectId) + "\n";
}
});
return report;
},
viewinfo: function(viewId) {
var report = "";
Java.perform(function() {
var radarAndroidClz = Java.use("gz.radar.Android");
var viewInfo = radarAndroidClz.getViewInfo(viewId + "");
if (!viewInfo) {
report += "Not Found Any Views."
return;
}
report += ("------------------View--------------------") + "\n";
report += ("View Id: " + viewInfo.getViewId()) + "\n";
report += ("View IdName: " + viewInfo.getViewIdName()) + "\n";
report += ("View Text: " + viewInfo.getViewText()) + "\n";
report += ("View Visible: " + viewInfo.isVisible()) + "\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";
}
});
return report;
},
appversion: function() {
var versionName = "";
Java.perform(function() {
var radarAndroidClz = Java.use("gz.radar.Android");
versionName = radarAndroidClz.getVersionName();
});
return versionName;
},
mainactivity: function() {
var mainactivityName = "";
Java.perform(function() {
var radarAndroidClz = Java.use("gz.radar.Android");
mainactivityName = radarAndroidClz.getMainActivity();
});
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();
}
});
================================================
FILE: js/spoof_signature.js
================================================
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;
}
});
}
================================================
FILE: js/ssl_log.js
================================================
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();
================================================
FILE: js/text_view.js
================================================
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;
};
}
});
================================================
FILE: js/trace_init_proc.js
================================================
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()
================================================
FILE: js/url.js
================================================
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;
};
});
================================================
FILE: js/webview_enable_debug.js
================================================
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);
================================================
FILE: mobile-deploy/daemon_app.sh
================================================
#!/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
================================================
FILE: mobile-deploy/frida-server-16.7.19-android-arm
================================================
[File too large to display: 21.0 MB]
================================================
FILE: mobile-deploy/frida-server-16.7.19-android-arm64
================================================
[File too large to display: 51.2 MB]
================================================
FILE: mobile-deploy/redsocks.conf
================================================
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;
}
================================================
FILE: requirements.txt
================================================
frida==16.7.19
frida-tools==13.7.1
adbutils==1.2.11
androguard==3.3.5
jsbeautifier==1.15.4
gitpython
loguru