[
  {
    "path": ".gitignore",
    "content": ".iml\n*injectXServer.js\n*spider.js\ncom.guazi*\ncom.ganji*\n*.python-version\n*/xinit/*.dex\n*pydevproject\n.DS_Store\nlog\n.*source/.*\ncom.example.myapplication/\n__handlers__/\nbuild/\n__pycache__\n*.jar\n.idea/\n.classpath\n.project\n.settings/\ntarget/\nlogs/\ndata/doc/\ndata/test/\n.vscode/\ncom.*\ncn.com.*\n"
  },
  {
    "path": ".hooker_driver",
    "content": "-U\n"
  },
  {
    "path": "README.md",
    "content": "[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/credittone-hooker-badge.png)](https://mseep.ai/app/credittone-hooker)\n\n免责声明\n本项目的所有内容仅供学习与技术交流使用，旨在帮助开发者理解移动应用的结构和工作原理。\n\n本项目不包含任何针对特定应用的破解操作或侵权内容。\n针对某些app存在的简单解包技术内容在各大技术论坛网站均大量存在，并无对这些app造成实际损害\n本项目无意协助任何非法用途，包括但不限于绕过版权保护、修改应用功能或获取未经授权的数据。\n请确保在使用本项目工具时遵守相关法律法规，并仅用于个人学习或研究目的。\n\n<p>简体中文 | <a href=\"README_EN.md\">English</a></p>\n\n# 欢迎使用hooker逆向工作台\n![GitHub stars](https://img.shields.io/github/stars/CreditTone/hooker?style=flat-square)\n![GitHub forks](https://img.shields.io/github/forks/CreditTone/hooker?style=flat-square)\n![GitHub code size](https://img.shields.io/github/languages/code-size/CreditTone/hooker?style=flat-square)\n![Python](https://img.shields.io/badge/python-3.8.8-blue?style=flat-square)\n![frida](https://img.shields.io/badge/frida-16.7.19-blue?style=flat-square)\n\nhooker是一个基于frida实现的逆向工具包。旨在为安卓逆向开发人员提供一个舒适的命令行界面和一些常用的通杀脚本、自动化生成hook脚本、内存漫游探测activity和service、frida版JustTrustMe、boringssl unpinning全网app通杀\n\n\n\n为什么你需要hooker？\n=================\n* [1. frida版JustTrustMe，通杀全网APP，且作者一直在持续维护升级](#12-frida版JustTrustMe包括boringgssl)\n* [2. 嵌入式webserver支持把App内部能力快速暴露成HTTP接口，便于做自动化和接口化](#7-嵌入式webserver)\n* [3. 快捷设置socks5代理，无需额外安装socksdroid等三方app实现无感知代理](#11-快捷设置socks5无感代理)\n\n<img src=\"https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg\" width=\"1000\">\n\n\n---\n\n目录\n=================\n\n* [快速开始](#快速开始)\n    * [1. git clone项目](#1-git-clone项目)\n    * [2. 安装依赖](#2-安装python3依赖)\n    * [3. root手机usb连接](#3-root手机usb连接PC)\n    * [4. 启动hooker](#4-启动hooker)\n    * [5. 输入调试应用包名](#5-输入调试应用包名)\n    * [6. 查看help信息](#6-查看help信息)\n    * [7. 嵌入式webserver](#7-嵌入式webserver)\n    * [8. 自动化生成frida脚本](#8-自动化生成frida脚本)\n    * [9. 列出所有frida脚本](#9-列出应用目录所有frida脚本)\n    * [10. attach执行frida脚本](#10-attach执行frida脚本)\n    * [11. 快捷设置socks5无感代理](#11-快捷设置socks5无感代理)\n    * [12. frida版JustTrustMe](#12-frida版JustTrustMe包括boringgssl)\n    * [13. spawn执行frida脚本](#13-spawn执行frida脚本)\n    * [14. 取消代理设置](#14-取消代理设置)\n    * [15. 重启app](#15-重启app)\n    * [16. 获取uid和pid](#16-获取uid和pid)\n    * [17. pull文件](#17-pull文件)\n    * [18. r0capture](#18-r0capture)\n    * [19. hooker自动升级](#19-upgrade)\n* [开发手机API接口](#开发手机api接口)\n* [hooker原生提供的操作API](#hooker原生提供的操作api)\n* [应用工作目录脚本](#应用工作目录脚本)\n    * [url.js](#urljs)\n    * [just_trust_me.js](#just_trust_mejs)\n    * [activity_events.js](#activity_eventsjs)\n    * [click.js](#clickjs)\n    * [android_ui.js](#android_uijs)\n    * [keystore_dump.js](#keystore_dumpjs)\n    * [edit_text.js](#edit_textjs)\n    * [hook_register_natives.js](#hook_register_nativesjs)\n    * [text_view.js](#text_viewjs)\n    * [ssl_log.js](#ssllogjs)\n    * [just_trust_me_for_ios.js](#just_trust_me_for_iosjs)\n    * [dump_dex.js](#dump_dexjs)\n    * [trace_init_proc.js](#trace_init_procjs)\n    * [hook_artmethod_register.js](#hook_artmethod_registerjs)\n    * [find_anit_frida_so.js](#find_anit_frida_sojs)\n    * [hook_jni_method_trace.js](#hook_jni_method_tracejs)\n    * [replace_dlsym_get_pthread_create.js](#replace_dlsym_get_pthread_createjs)\n    * [find_boringssl_custom_verify_func.js](#find_boringssl_custom_verify_funcjs)\n    * [get_device_info.js](#get_device_infojs)\n    * [apk_shell_scanner.js](#apk_shell_scannerjs)\n    * [bypass_frida_svc_detect.js](#bypass_frida_svc_detectjs)\n    * [bypass_root_detect.js](#bypass_root_detectjs)\n    * [bypass_vpn_detect.js](#bypass_vpn_detectjs)\n    * [hook_encryption_algo.js](#hook_encryption_algojs)\n    * [hook_encryption_algo2.js](#hook_encryption_algo2js)\n    * [webview_enable_debug.js](#webview_enable_debugjs)\n* [Windows安装WSL](#windows安装wsl)\n\t  * [1. 安装wsl ubuntn24.04](#1-安装wsl-ubuntn2404)\n\t  * [2. 进入wsl，配置代理](#2-进入wsl配置代理)\n\t  * [3. 安装python3.8和frida](#3-安装python38和frida)\n* [自定义frida-server](#自定义frida-server)\n* [hooker命令行快捷键](#hooker命令行快捷键)\n\n    \n    \n手机保证root，无需任何手动启动frida-server等一切配置，hooker会帮你搞定一切。x86架构的模拟器不兼容\n\n# Mac/Linux配置hooker运行环境\n\nWindows请先完成[WSL安装](#windows%E5%AE%89%E8%A3%85wsl)，然后跳回到这里\n\n### 1. git clone项目\n```shell\nstephen@Mac:~$ git clone https://github.com/CreditTone/hooker.git\nstephen@Mac:~$ cd hooker\n```\n\n### 2. 安装python3依赖\n```shell\nstephen@Mac:~/hooker$ pip3 install -r requirements.txt\n```\n\n\n### 3. root手机usb连接PC\n```shell\nstephen@Mac:~/hooker$ adb devices\nList of devices attached\nFA77C0301476\tdevice\n```\n\n\n### 4. 启动hooker\n\n这里注意，不要用绝对路径去执行，一定要cd到hooker目录下执行python3 hooker.py\n\nhooker启动后将收集所有可调试app的信息，字段含义如下\n- PID：当前app的主进程id，如果app没有启动则为0\n- APP：app的名称\n- IDENTIFIER：app的包名\n- EXIST_REVERSE_DIRECTORY：如果app曾经被调试过就是✅，从没被调试过就是❌\n\n```shell\nstephen@Mac:~/hooker$ python3 hooker.py\nhooker Let's enjoy reverse engineering together\n-----------------------------------------------------------------------------------------------\nPID   \tAPP                 \tIDENTIFIER                         \tEXIST_REVERSE_DIRECTORY\n0     \t全球上网            \tcom.miui.virtualsim                \t❌\n0     \t爱奇艺              \tcom.qiyi.video                     \t❌\n0     \t红手指云手机        \tcom.redfinger.app                  \t❌\n0     \tReqable             \tcom.reqable.android                \t❌\n0     \t美团                \tcom.sankuai.meituan                \t✅\n0     \t得物                \tcom.shizhuang.duapp                \t❌\n0     \t某皮           \t     cxm.shxpxx.sg                      \t✅\n0     \t微博                \tcom.sina.weibo                     \t❌\n0     \t今日头条            \tcom.ss.android.article.news        \t✅\n0     \t西瓜视频            \tcom.ss.android.article.video       \t✅\n0     \t懂车帝              \tcom.ss.android.auto                \t✅\n0     \t抖音火山版          \tcom.ss.android.ugc.live            \t✅\n0     \t抖音精选            \tcom.ss.android.yumme.video         \t❌\n0     \t淘宝                \tcom.taobao.taobao                  \t✅\n0     \t腾讯视频            \tcom.tencent.qqlive                 \t❌\n0     \tTermux              \tcom.termux                         \t❌\n0     \t轻奢                \tcom.tm.bachelorparty               \t✅\n0     \tWiFi ADB            \tcom.ttxapps.wifiadb                \t❌\n0     \tVMOS Pro            \tcom.vmos.pro                       \t✅\n0     \t游戏中心            \tcom.xiaomi.gamecenter              \t❌\n0     \t小米商城            \tcom.xiaomi.shop                    \t❌\n0     \t米家                \tcom.xiaomi.smarthome               \t❌\n0     \t小米有品            \tcom.xiaomi.youpin                  \t✅\n0     \t小红书              \tcom.xingin.xhs                     \t✅\n0     \t运满满货主          \tcom.xiwei.logistics.consignor      \t✅\n0     \t拼多多              \tcom.xunmeng.pinduoduo              \t✅\n0     \tEnvCheck            \tcom.yimian.envcheck                \t✅\n0     \tcheck_env           \tcom.yuuki.check_env                \t❌\n0     \tTikTok              \tcom.zhiliaoapp.musically           \t❌\n0     \tXPrivacyLua         \teu.faircode.xlua                   \t❌\n0     \timToken             \tim.token.app                       \t❌\n0     \tSocksDroid          \tnet.typeblog.socks                 \t❌\n0     \tF-Droid             \torg.fdroid.fdroid                  \t❌\n0     \tProxyDroid          \torg.proxydroid                     \t❌\n3457  \t手机管家            \tcom.miui.securitycenter            \t✅\n3509  \t优信拍              \tcom.uxin.buyerphone                \t✅\n18780 \t抖音                \tcom.ss.android.ugc.aweme           \t✅\n20174 \t应用商店            \tcom.xiaomi.market                  \t❌\n20913 \t设置                \tcom.android.settings               \t❌\n30500 \t小爱同学            \tcom.miui.voiceassist               \t❌\n32163 \t相机                \tcom.android.camera                 \t✅\nPlease enter the identifier that needs to be reversed\nhooker(Identifier):\n```\n***\n\n\n### 5. 输入调试应用包名\n\n- 输入调试应用包名回车后，如果是第一次调试应用，hooker将创建应用目录，应用目录名称为应用的Identifier，用于存放所有js脚本和快捷命令。\n\n- hooker将帮你检测当前app是否启动且在手机前台，如不在启动帮你启动，如不在前台帮你切到前台\n\n- frida通杀脚本可以在hooker交互式命令行下用attach/spawn执行，也可以手动cd到应用目录用快捷命令或原生的frida命令执行。\n\n- 你可以修改应用工作目录下任何脚本\n\n\n```shell\nhooker(Identifier): cxm.shxpxx.sg\n✅ App cxm.shxpxx.sg is already in the foreground\nCreating working directory: cxm.shxpxx.sg\nGenerating frida shortcut command...\nGenerating built-in frida script...\npull /data/app/cxm.shxpxx.sg-L8zkrpFVICv0-hOrtmPPxA==/base.apk to cxm.shxpxx.sg/ShopeeSG_3.43.40.apk successful\nWorking directory create successful\njust_trust_me.js                                 empty.js                                         keystore_dump.js\nedit_text.js                                     activity_events.js                               find_boringssl_custom_verify_func.js\nssl_log.js                                       hook_register_natives.js                         click.js\nget_device_info.js                               apk_shell_scanner.js                             dump_dex.js\nobject_store.js                                  hook_artmethod_register.js                       replace_dlsym_get_pthread_create.js\njust_trust_me_for_ios.js                         trace_initproc.js                                android_ui.js\nhook_jni_method_trace.js                         url.js                                           just_trust_me_okhttp_hook_finder_for_android.js\ntext_view.js                                     find_anit_frida_so.js\n某皮 > \n```\n![hooker_enter_debug.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_enter_debug.gif)\n***\n\n\n\n\n### 6. 查看help信息\n\n在使用hooker过程中，如不记得命令，可随时调出help查看操作手册。\n\n```shell\n某皮 > help\nh, help                                      show this help message\na, activitys                                 show the activity stack\ns, services                                  show the service stack\no, object [object_id]                        show object info by object_id\nv, view [view_id]                            show view info by view_id of view\ngs, generatescript [class_name:method_name]  specify the class name and method name to generate a frida hook java script file. For example: generatescript\n                                             okhttp3.Request$Builder:addHeader\np, proxy [socks5_proxy_server]               set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998\nup, unproxy                                  remove socks5 proxy for this app\ntrust, justtrustme                           quickly spawn just_trust_me.js script to kill all ssl pinning\nls                                           list all the frida scripts of the current app\nattach [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\nspawn [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\n                                             just_trust_me.js\nrestart                                      restart this app\npid                                          get pid of this app main process\nuid                                          get pid of this app\nexit                                         return to the previous level\n某皮 > \n```\n![hooker_help.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_help.gif)\n***\n\n\n\n### 7. 嵌入式webserver\n\nhooker 支持在手机中注入一个轻量级 webserver。启动后会在目标 App 进程内开启一个 HTTP 服务，默认端口是 `8080`。这个服务既可以暴露 patch 项目里自定义的 controller，也会自动挂载一组内置调试接口。\n\n- 启动内置 webserver\n\n```shell\n某音火山版 > webserver start\nHttp server port: 8080\nHttp server: http://10.112.101.249:8080\n```\n\n启动内置 webserver 后，浏览器打开首页即可看到当前已注册的 API 列表。常用能力包括：\n\n- 服务管理：`/` 查看欢迎页和接口清单，`/stop` 停止当前 webserver。\n- UI 自动化：`/hooker/ui/...` 提供点击控件、按文本点击、按坐标点击、设置输入框文本、触发返回/Home、启动 Activity、获取屏幕信息、翻页、滚动 RecyclerView、尝试关闭弹窗等能力，适合做半自动化操作和页面联调。\n- 页面结构导出：`/hooker/uiauto/dump`、`/hooker/uiauto/window_dump.xml`、`/hooker/uiauto/window_dump.json` 可以导出当前窗口层级，方便定位控件和分析页面结构。\n- 截图能力：`/hooker/screencap/screenshot` 可直接调用系统 `screencap` 截图；`/hooker/mediaprojection/...` 支持申请 MediaProjection 权限并抓取整屏 PNG，更适合做远程观察和自动化闭环。\n- App 信息读取：`/hooker/appinfo` 可读取包名、版本、组件、权限、签名和目录信息；`/hooker/appinfo/shared_prefs`、`/hooker/appinfo/databases`、`/hooker/appinfo/read_table` 可以直接查看 `shared_prefs`、数据库结构和表数据。\n- 类/对象辅助调用：`/hooker/classhelper/invoke_static_method`、`/hooker/classhelper/invoke_method` 支持通过 HTTP 直接调用静态方法或已缓存对象的方法，便于快速验证算法、补环境或调试业务逻辑。\n- 文件回传：`/file?filename=...` 可以把绝对路径文件或 webserver 缓存目录里的文件直接通过 HTTP 返回。\n- MCP 风格 UI 工具：`/hooker/mcp/ui/tools` 和 `/hooker/mcp/ui/call` 把常用 UI 操作封装成统一工具接口，便于外部脚本或 Agent 通过 HTTP 驱动当前 App。\n\n\n- 7.1 启动自定义 webserver\n\n给定一个patch工程的jar包，将爬虫接口启动为webserver。\n\n```shell\n某宝 > webserver start taxbax-patch.jar\nConverting taxbax-patch.jar to taxbax-patch.dex...\nSuccessfully converted to taxbax-patch.dex (7160 bytes)\npush file OK /data/user/0/com.taxbax.taxbax/hooker_server.dex\nHttp server port: 2026\nHttp server: http://10.112.101.249:2026\n```\n\n这里的 `taxbax-patch.jar` 可以理解成“运行在目标 App 进程里的业务插件”。`hooker` 会先把 jar 转成 dex，再注入到目标进程中，扫描其中带注解的类，然后把这些类注册成 HTTP 路由。\n\n- 7.2 自定义 webserver 适合做这类事情：\n\n- 暴露目标 App 内部已经存在的业务能力，比如搜索、评论、详情页、签名、加解密、用户资料、直播接口等。\n- 直接复用目标 App 自己的登录态、网络栈、环境参数和对象实例，避免在外部重复补协议。\n- 把异步回调、Observable、Listener、页面对象调用收敛成一个同步 HTTP 接口，对外统一返回 JSON 或文本。\n\n你可以像开发springboot一样优雅的开发嵌入式的httpserver，并调用目标app的任何java代码而无需像有些xposed插件一样TMD的反射、反射、再反射，跟SB一样😊\n\n- 用 `@HookerWebServerConfiguration(port = 2026)` 指定端口，不写时默认走 `8080`。\n- 用 `@HookerController(\"/taobao\")`、`@HookerController(\"/douyin\")` 这类注解定义业务前缀。\n- 用 `@HookerRequestMapping(path = \"/getProductDetail\")` 之类的注解暴露具体接口。\n- 用 `@HookerRequestParam`、`@HookerRequestPostJson` 接收查询参数和 POST JSON。\n\n\n具体开发文档，将在https://github.com/CreditTone/radar4hooker详细介绍\n\n\n- 7.3 webserver 持久化\n\n如果你已经为某个 App 开发好了 patch 工程，并且需要在多台设备上长期部署，那么 `frida + hooker` 的临时注入方式会比较重。针对这种场景，作者提供了 Xposed 插件 `HookerServer`：\n\n`https://github.com/CreditTone/HookerServer`\n\n只要手机支持 Xposed/LSPosed，就可以借助这个插件把 webserver 持久化到目标 App 中。\n\n操作步骤：\n\n1. 从 `https://github.com/CreditTone/HookerServer/releases` 下载最新 APK。\n2. 把 `patch.dex` 推送到 `/data/user/0/{package}/hooker_server.dex`。\n3. 在 Xposed/LSPosed 中启用 `HookerServer`，并勾选目标 App。\n4. 重启 App，webserver 即可自动启动。\n\n其中 `patch.dex` 可以在第一次部署 `patch.jar` 后获得，hooker 会自动完成 jar 转 dex，并把生成结果放到目标应用的工作目录中。\n\n\n***\n\n\n\n### 8. 自动化生成frida脚本\n自动化生成脚本是hooker的杀器。虽然现在AI大模型也可以写，但是我们离内存近，更快，也不需要联网。生成的脚本自带打印堆栈等信息，和一些你可能需要的扩展方法。\n另外在生成脚本的过程中，命令行类名、方法名提示也可以当作搜索使用，能通过关键词快速搜索定位类方法。hooker搜索类比jadx快很多，不信就试试......\n\n![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg)\n- Command语法：gs, generatescript [class_name:method_name]\n\n\n- 8.1 生成指定方法的frida hook脚本：\ngs okhttp3.Request$Builder:addHeader，参数部分(String, String)不是必须写的\n\n```shell\n某信拍 > gs okhttp3.Request$Builder:addHeader(String, String)\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder.addHeader.js\n某信拍 > \n```\n\n```js\n//cat okhttp3.Request.Builder.addHeader.js\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_method_addHeader_2grl = okhttp3_Request_Builder_clz.addHeader.overload('java.lang.String', 'java.lang.String');\n    okhttp3_Request_Builder_clz_method_addHeader_2grl.implementation = function(string, string_x2) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.addHeader(java.lang.String,java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_addHeader_2grl.call(this, string, string_x2);\n        console.log(\"header name:\" + string + \" header value:\" + string_x2);\n        printBeat(beat);\n        return ret;\n    };\n});\n```\n***\n\n\n\n- 8.2 生成指定类的所有成员方法的frida hook脚本：\ngs okhttp3.Request$Builder\n\n```shell\n某信拍 > generatescript okhttp3.Request$Builder\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder.allfunc.js\n```\n***\n\n```js\n//cat okhttp3.Request.Builder.allfunc.js\n//okhttp3.Request$Builder\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_method_header_ng3n = okhttp3_Request_Builder_clz.header.overload('java.lang.String', 'java.lang.String');\n    okhttp3_Request_Builder_clz_method_header_ng3n.implementation = function(string, string_x2) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.header(java.lang.String,java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_header_ng3n.call(this, string, string_x2);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_cacheControl_q8q5 = okhttp3_Request_Builder_clz.cacheControl.overload('okhttp3.CacheControl');\n    okhttp3_Request_Builder_clz_method_cacheControl_q8q5.implementation = function(cacheControl) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.cacheControl(okhttp3.CacheControl)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_cacheControl_q8q5.call(this, cacheControl);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_method_bjk9 = okhttp3_Request_Builder_clz.method.overload('java.lang.String', 'okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_method_bjk9.implementation = function(string, requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.method(java.lang.String,okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_method_bjk9.call(this, string, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_head_a5nq = okhttp3_Request_Builder_clz.head.overload();\n    okhttp3_Request_Builder_clz_method_head_a5nq.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.head()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_head_a5nq.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_headers_to5i = okhttp3_Request_Builder_clz.headers.overload('okhttp3.Headers');\n    okhttp3_Request_Builder_clz_method_headers_to5i.implementation = function(headers) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.headers(okhttp3.Headers)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_headers_to5i.call(this, headers);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_post_heaq = okhttp3_Request_Builder_clz.post.overload('okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_post_heaq.implementation = function(requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.post(okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_post_heaq.call(this, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_build_rmqx = okhttp3_Request_Builder_clz.build.overload();\n    okhttp3_Request_Builder_clz_method_build_rmqx.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request okhttp3.Request$Builder.build()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_build_rmqx.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_patch_hp9u = okhttp3_Request_Builder_clz.patch.overload('okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_patch_hp9u.implementation = function(requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.patch(okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_patch_hp9u.call(this, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_url_0owi = okhttp3_Request_Builder_clz.url.overload('java.lang.String');\n    okhttp3_Request_Builder_clz_method_url_0owi.implementation = function(string) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_url_0owi.call(this, string);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_removeHeader_uzb9 = okhttp3_Request_Builder_clz.removeHeader.overload('java.lang.String');\n    okhttp3_Request_Builder_clz_method_removeHeader_uzb9.implementation = function(string) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.removeHeader(java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_removeHeader_uzb9.call(this, string);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_url_ykbd = okhttp3_Request_Builder_clz.url.overload('java.net.URL');\n    okhttp3_Request_Builder_clz_method_url_ykbd.implementation = function(url) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.net.URL)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_url_ykbd.call(this, url);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_delete_dqyl = okhttp3_Request_Builder_clz.delete.overload();\n    okhttp3_Request_Builder_clz_method_delete_dqyl.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.delete()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_delete_dqyl.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    //.......省略N行代码\n```\n***\n\n\n\n- 8.3 生成指定类的构造方法的frida hook脚本：\ngs okhttp3.Request$Builder:_ 或者gs okhttp3.Request$Builder:\\<init\\>\n\n```shell\n某信拍 > gs okhttp3.Request$Builder:<init>()\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder._init.js\n```\n\n```js\n//cat okhttp3.Request.Builder._init.js\n//okhttp3.Request$Builder:<init>()\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_init_uw3i = okhttp3_Request_Builder_clz.$init.overload();\n    okhttp3_Request_Builder_clz_init_uw3i.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder()';\n        var beat = newMethodBeat(beatText, executor);\n        var returnObj = okhttp3_Request_Builder_clz_init_uw3i.call(this);\n        printBeat(beat);\n        return returnObj;\n    };\n    var okhttp3_Request_Builder_clz_init_e58t = okhttp3_Request_Builder_clz.$init.overload('okhttp3.Request');\n    okhttp3_Request_Builder_clz_init_e58t.implementation = function(v0) {\n        var executor = this.hashCode();\n        var beatText = 'okhttp3.Request$Builder(okhttp3.Request)';\n        var beat = newMethodBeat(beatText, executor);\n        var returnObj = okhttp3_Request_Builder_clz_init_e58t.call(this, v0);\n        printBeat(beat);\n        return returnObj;\n    };\n});\n```\n***\n\n\n\n\n### 9. 列出应用目录所有frida脚本\n\n- 查看应用目录下所有的脚本，这里有hooker给你生成的通杀脚本，也有您生成的指定hook脚本，您可以修改定制。\n\n- 如果您有自定义的脚本放到应用目录下，ls命令可以将脚本名称刷入命令行提示缓存\n\n```shell\n某皮 > ls\njust_trust_me.js                                 empty.js                                         keystore_dump.js\nokhttp3.Request.Builder.addHeader.js             edit_text.js                                     activity_events.js\nfind_boringssl_custom_verify_func.js             ssl_log.js                                       hook_register_natives.js\nclick.js                                         get_device_info.js                               apk_shell_scanner.js\ndump_dex.js                                      object_store.js                                  hook_artmethod_register.js\nreplace_dlsym_get_pthread_create.js              just_trust_me_for_ios.js                         trace_initproc.js\nandroid_ui.js                                    hook_jni_method_trace.js                         url.js\njust_trust_me_okhttp_hook_finder_for_android.js  text_view.js                                     find_anit_frida_so.js\n某皮 >\n```\n***\n\n### 10. attach执行frida脚本\n\n在hooker下执行frida脚本，您只需要输入attach【空格】脚本名称会自动弹出提示。上下选择需要的脚本按tab键即可自动输入全名称。\n这是hooker在追求极致的工匠精神，帮助您从开波音737到开空客320的跳跃。\n\n> **日志同步**：`attach` / `frida` 执行脚本时，Frida 脚本的 `send()` 输出会自动同步保存到应用工作目录下的同名 `.log` 文件。例如 `attach url.js` 会生成 `url.log`，方便喂给 AI 分析。\n\n```shell\n某信拍 > attach url.js\nFrida output logging -> com.uxin.buyerphone/url.log\n------------startFlag:0755liv1,objectHash:-915348569,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836814835---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836814832&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\npublic java.net.URL(String)\n\tat java.net.URL.<init>(Native Method)\n\tat com.wxbx.wmda.e.b.a(SourceFile:5)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:0755liv1,usedtime:1---------------\n\n------------startFlag:1ps6go99,objectHash:-237375819,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815192---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\npublic java.net.URL(String)\n\tat java.net.URL.<init>(Native Method)\n\tat com.android.okhttp.HttpUrl.url(HttpUrl.java:327)\n\tat com.android.okhttp.Request.url(Request.java:53)\n\tat com.android.okhttp.Request$Builder.build(Native Method)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258)\n\tat com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)\n\tat com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26)\n\tat com.wxbx.wmda.e.b.a(SourceFile:14)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:1ps6go99,usedtime:0---------------\n// 这里省略无数日志.............\n------------startFlag:i7osxvjl,objectHash:134280600,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815193---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\ncom.android.okhttp.Request.Builder.build()\n\tat com.android.okhttp.Request$Builder.build(Native Method)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258)\n\tat com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)\n\tat com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26)\n\tat com.wxbx.wmda.e.b.a(SourceFile:14)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:i7osxvjl,usedtime:1---------------\n// 这里省略无数日志.............\n```\n***\n\n### 11. 快捷设置socks5无感代理\n\n通过iptables链路层转发包实现一键设置代理，优势是APP完全无感知被代理。推荐使用charles的socks5\n\n设置代理后必须主动去[关闭代理](#14-取消代理设置)，代理不会自动取消\n\n\n```shell\n某音 > proxy socks5://10.112.99.11:9998\nproxy socks5://10.112.99.11:9998 OK\n某音 > \n```\n***\n\n\n### 12. frida版JustTrustMe（包括boringgssl）\n\n关掉SSL证书校验\n\n```shell\n某音 > justtrustme\nPackage name: com.ss.xxxx.xxx.aweme\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\nstatic 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!\nstatic 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!\nstatic 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!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nstatic 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!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\n// 这里省略无数日志.............\n```\n***\n\n\n### 13. spawn执行frida脚本\n\n> **日志同步**：`spawn` / `fridaf` 执行脚本时，Frida 脚本的 `send()` 输出同样会自动同步保存到同名 `.log` 文件。例如 `spawn just_trust_me.js` 会生成 `just_trust_me.log`。\n\n```shell\n某信拍 > spawn just_trust_me.js\nFrida output logging -> com.uxin.buyerphone/just_trust_me.log\nPackage name: com.xxx.buyxxphone\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\n// 这里省略无数日志.............\n```\n***\n\n\n### 14. 取消代理设置\n\n一键取消代理\n\n```shell\n某音 > unproxy\nunproxy OK\n某音 > \n```\n***\n\n\n\n### 15. 重启app\n\n\n```shell\n某信拍 > restart\nrestarts com.xxx.buyxxphone\n```\n***\n\n\n### 16. 获取uid和pid\n\n```shell\n某信拍 > uid\n10189\n某信拍 > pid\n3509\n```\n***\n\n\n### 17. pull文件\n\n快捷拉取文件到本地应用工作目录\n\n```shell\n转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libmsaoaidsec.so\npull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libmsaoaidsec.so to com.xxx.zhuanmou/libmsaoaidsec.so successful\n转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libsignLib.so\npull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libsignLib.so to com.xxx.zhuanmou/libsignLib.so successful\n转某 > pull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libweconvert.so\npull /data/app/com.xxx.zhuanmou-o1ZYFILxnOCIpvvqJQKrpQ==/lib/arm64/libweconvert.so to com.xxx.zhuanmou/libweconvert.so successful\n```\n***\n\n\n\n\n### 18. r0capture\nhooker集成了r0capture，抓包产生的pcap文件保存在{应用包名}/r0capture_ssl.pcap路径下，如酷安：com.coolapk.market/r0capture_ssl.pcap\n\n```shell\nJqgvBRe45o4QLyGguX+eVDoN0CPLTcPXqRVBhh13z2PTch2W7Hgv\\/xlp4x2v\\/QemWXrjWuifc2el1gzK1+8YPW+1NyTFCC8P10+zpCAPRgBwxpjKp4ecSQngU32yY2daIbaEwj0fvAg12VZNCdtI8jtpGtgds5xe61cihcBaYg\\/CTvUIEylZqE6cbWsbuiBf7OuJLAnofXi3JtUaD+kJxFQ4fsZOTxhpZqANHIVv17GPcG4CoJEMws8UzawN3xPMqVYdzv+bpAnbDRhZy6LsVxS5S6yYtrawQdroJqVfsaLXlzgTBQe6RVPYqWG38QKJ1cuOGttk0ukigGIce3QAUcJl0c3fsi973ydYnSY60PBSKumqZAFh4VM0jk5tmRUtZlrqfQDVmfgIaocPQ==\"}\n2025-06-19 11:35:59.794 | INFO     | __main__:r0capture_on_message:939 - java.lang.Throwable\n\tat com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream.write(Native Method)\n\tat com.android.okhttp.okio.Okio$1.write(Okio.java:76)\n\tat com.android.okhttp.okio.AsyncTimeout$1.write(AsyncTimeout.java:155)\n\tat com.android.okhttp.okio.RealBufferedSink.flush(RealBufferedSink.java:221)\n\tat com.android.okhttp.internal.http.Http1xStream.finishRequest(Http1xStream.java:161)\n\tat com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:735)\n\tat com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:609)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:471)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:407)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:244)\n\tat com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)\n\tat com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:26)\n\tat com.tencent.msdk.dns.core.rest.share.a.a(AbsHttpDns.java:23)\n\tat com.tencent.msdk.dns.core.k$a.run(LookupHelper.java:24)\n\tat com.tencent.msdk.dns.core.c$b.run(CountDownManager.java:3)\n\tat com.tencent.msdk.dns.core.c$a.run(CountDownManager.java:3)\n\tat com.tencent.msdk.dns.base.executor.DnsExecutors$a.run(DnsExecutors.java:38)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n\tat java.lang.Thread.run(Thread.java:764)\n\nCTRL + C to stop >\nInterrupting\nflushing com.coolapk.market/r0capture_ssl.pcap successful\nr0capture.js detach successful\nRestarting 酷安 Please wait for a few seconds\n```\n***\n\n\n### 19. upgrade\n\nhooker更新频繁，平均周更约10次。upgrade帮助您随时同步最新代码和相关文件到本地。\n\n```shell\nMacBook-Pro-32G-2T:hooker stephen256$ python3 hooker.py upgrade\nUpgrading hooker\nRepository updated with 'git pull'.\nUpdating mobile-deploy/libext64.so\nUpdating mobile-deploy/libext.so\nUpdating mobile-deploy/libevent-2.1.so\nPlease restart hooker\n```\n***\n\n\n# 应用目录通杀脚本\n\n### url.js\n\n该脚本会 hook 应用中 构造 URL 或 URI 对象的多个关键方法，用于打印或分析网络请求相关的信息（如目标 URL）\n\nHook 的目标方法\njava.net.URI(String) 构造函数\n\njava.net.URL(String) 构造函数\n\nokhttp3.Request.Builder.build() 方法（常用于创建 HTTP 请求）\n\ncom.android.okhttp.Request.Builder.build()（系统自带 okhttp）\n\nandroid.net.Uri.parse(String) 方法（处理 URI 的常用工具方法）\n\n推荐命令：frida url.js\n\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/url.jpg)\n***\n\n### just_trust_me.js\nfrida版本的just_trust_me，用于绕过 Android 系统中的证书校验逻辑，允许抓取 HTTPS 流量，适用于基于 OkHttp、HttpURLConnection 等网络库的应用。\n\n支持boringssl unpinning，理论上支持全网所有app，除非像美团一样做了登录风控抓不了。\n\n在hooker命令行模式封装了快捷命令justtrustme\n这在上文 [执行justtrustme kill掉所有ssl验证](#11-执行justtrustme-kill掉所有ssl验证包括boringgssl)有介绍\n\n亦可直接执行脚本 spawn just_trust_me.js\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/just_trust_me.gif)\n***\n\n### activity_events.js\n当你需要跟踪start某个Activity启动时可执行，获取startActivity的intent信息和调用堆栈。\n\n推荐命令：frida activity_events.js\n\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/activity_events.gif)\n***\n\n### click.js\n用于监听 Android 应用中点击事件（`OnClickListener`），主要用于分析用户交互操作，获取被点击View的真实ViewClass\n\n推荐命令：frida click.js\n\n\n***\n\n### android_ui.js\n封装一些操作原生Android UI的函数。如startActivity(activityName)、home()、back()、finishCurrentActivity()、clickByText(text) 等等，命令使用得用attach './attach android_ui.js' 原理是借助radar.dex作为代理操作Android原生View。\n\n推荐命令：frida android_ui.js\n\n***\n\n### keystore_dump.js\n在https双向认证的情况下，dump客户端证书为p12。存储位置:/data/user/0/{packagename}/client_keystore_{nowtime}.p12 证书密码: hooker。原理是hook java.security.KeyStore的getPrivateKey和getCertificate方法，因为客户端向服务发送证书必调这个方法。强烈建议keystore_dump.js用spawn模式启动，\n\n推荐命令：spawn keystore_dump.js\n\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_01.png)\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_02.png)\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_03.png)\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/https_bothway_04.png)\n***\n\n### edit_text.js\n\n用于跟踪获取 EditText 的 `getText()` 事件，并获取其真实 Class 类型。  \nEditText 通常绑定搜索按钮或输入事件，是定位“搜索”接口实现代码的有效入口，辅助识别核心业务逻辑。\n\n推荐命令：frida edit_text.js\n\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/edit_text.png)\n***\n\n### hook_register_natives.js\n用于拦截 Android JNI RegisterNatives 函数的 Frida 脚本，主要用于分析和修改原生方法(native methods)的注册过程\n\n推荐命令：spawn hook_register_natives.js\n\n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/hook_RN.gif)\n***\n\n### text_view.js\n\n用于跟踪 TextView 的 `setText()` 和 `getText()` 调用，并输出其真实 Class。  \n常用于提取页面展示的明文数据，以及获取堆栈调用信息，从而追踪业务层的 model 构造逻辑和数据源。\n\n推荐命令：spawn/attach text_view.js  \n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/text_view.png)\n\n---\n\n\n### activity_events.js\n\n用于跟踪 Android Activity 生命周期（如 `onCreate`、`onResume`）的 Frida 脚本，帮助分析 Activity 初始化逻辑。\n\n推荐命令：spawn/attach activity_events.js\n\n---\n\n\n### ssl_log.js\n\n用于在 native 层跟踪 SSL 握手过程并记录 `CLIENT_RANDOM`，配合 tcpdump 抓包数据后可用于 TLS 明文还原分析。\n\n推荐命令：spawn/attach ssl_log.js\n\n---\n\n### just_trust_me_for_ios.js\n\niOS 版的证书校验绕过脚本，配合抓包代理使用，适用于 SSL Pinning 场景。\n\n推荐命令：spawn/attach just_trust_me_for_ios.js\n\n---\n\n### dump_dex.js\n\n执行 spawn dump_dex.js 可直接脱壳，适用于多数简单壳场景。  \nAndroid ART 使用 dex2oat 编译 DEX 为 native 指令，有些脱壳失败时建议手动清除 `/data/app/<package>-*/oat/arm64/`。\n\n推荐命令：spawn dump_dex.js\n\n---\n\n### trace_init_proc.js\n\n用于 trace `init_proc` 函数调用流程的脚本。  \n需手动指定 `startAddr`、`endAddr` 和模块名 `somodule`，适合分析 native 启动流程。\n\n推荐命令：spawn/attach trace_init_proc.js  \n![trace_init_proc.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/trace_init_proc.png)\n\n---\n\n### hook_artmethod_register.js\n\n用于拦截 Android ART 虚拟机中 `ArtMethod` 的注册函数，适合深入分析虚拟机行为与 native 方法绑定。\n\n推荐命令：spawn/attach hook_artmethod_register.js\n\n---\n\n### find_anit_frida_so.js\n\n用于发现 app 中加载的可疑 anti-frida 动态库，识别顺序为：**谁最后加载、谁让 app 崩溃，谁就是反调试的嫌疑人**。\n\n推荐命令：spawn/attach find_anit_frida_so.js  \n![find_anti_frida_so.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/find_anti_frida_so.png)\n\n---\n\n### hook_jni_method_trace.js\n\n用于追踪 Native 层回调 Java 方法的行为，可观察 so 层与 Java 的交互，帮助分析 JNI 层调用栈。\n\n推荐命令：spawn/attach hook_jni_method_trace.js\n\n---\n\n### replace_dlsym_get_pthread_create.js\n\n专门对抗 `libmsaoaidsec.so` 中使用 `dlsym` 获取 `pthread_create` 的反调试方式。  \n本脚本 hook `dlsym`，用于劫持线程创建行为，从而对抗动态加载的反调试代码。\n\n推荐命令：spawn/attach replace_dlsym_get_pthread_create.js  \n![replace_pthread_create.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/replace_pthread_create.png)\n\n---\n\n### find_boringssl_custom_verify_func.js\n\n用于查找 `boringssl` 中注册的自定义证书验证函数，通过 hook `SSL_CTX_set_custom_verify` 定位目标函数，并实现强制信任（返回 0）。\n\n**使用建议：**  \n执行前请清除目标 app（如某音）的缓存，以保证函数重新注册。\n\n推荐命令：spawn find_boringssl_custom_verify_func.js  \n![find_boringssl_custom_verify.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/find_boringssl_custom_verify.png)  \n![hook_verify.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/hook_verify.png)  \n![mouyin_capture_33.9.0.png](https://raw.githubusercontent.com/CreditTone/img_resources/main/mouyin_capture_33.9.0.png)\n\n---\n\n### get_device_info.js\n\n用于全面获取设备指纹信息，包括：\n\n- Android ID、IMEI、指纹、厂商、系统信息\n- 安装的所有应用（含系统 app）\n- 传感器信息（如名称、厂商、延迟）\n- 系统状态（是否 root、运行时、内核信息等）\n\n推荐命令：attach get_device_info.js  \n![](https://raw.githubusercontent.com/CreditTone/img_resources/main/get_device_info_attach.png)\n\n提供以下 4 个调用方法：\n\n- `getBasicInfo()`：基础信息\n- `getInstalledPackages()`：应用列表\n- `getSensos()`：传感器信息\n- `getSystemInfo()`：系统与运行环境  \n  ![](https://raw.githubusercontent.com/CreditTone/img_resources/main/get_device_info_functions.png)\n\n---\n\n### apk_shell_scanner.js\n\n动态识别 APK 加壳技术的脚本，支持多种主流壳，如娜迦、爱加密、360、梆梆、腾讯御、网易易盾等。\n\n推荐命令：attach apk_shell_scanner.js\n\n识别结果示例：\n- This app is protected by {爱加密}\n- This app is not protected or uses an unknown protection scheme\n\n---\n\n### bypass_frida_svc_detect.js\n\n绕过 app 对 Frida Server 的检测逻辑，适用于反调试保护较强的目标应用。\n\n推荐命令：spawn/attach bypass_frida_svc_detect.js\n\n---\n\n### bypass_root_detect.js\n\n用于绕过 root 检测逻辑，使得 root 环境下也能正常运行 app。\n\n推荐命令：spawn/attach bypass_root_detect.js\n\n---\n\n### bypass_vpn_detect.js\n\n用于绕过 VPN 检测逻辑，避免 app 阻止使用代理、VPN 或抓包工具。\n\n推荐命令：spawn/attach bypass_vpn_detect.js\n\n---\n\n### hook_encryption_algo.js\n\n用于 hook 典型加密算法（如 AES、RSA、HMAC 等）的实现函数，分析加密参数、明文与密文数据。\n\n推荐命令：spawn/attach hook_encryption_algo.js\n\n---\n\n### hook_encryption_algo2.js\n\n拓展版本的加密算法 hook 脚本，适用于更复杂的加密场景或多路加密调用链。\n\n推荐命令：spawn/attach hook_encryption_algo2.js\n\n### webview_enable_debug.js\n\n该脚本用于 Hook Android 应用中的 WebView 行为，特别是在 WebView 初始化和执行 JavaScript 时进行监控和调试。适用于分析 WebView 加载的页面、动态注入的 JavaScript 代码，以及定位 JS 调用位置，常用于逆向分析、抓取数据或识别加固方案中的 Web 容器。\n\n推荐命令：spawn webview_enable_debug.js\n\n<img src=\"https://raw.githubusercontent.com/CreditTone/img_resources/main/webview_debugging.png\" width=\"1000\">\n\n# Windows安装WSL\n\nWSL是适用于Linux 的Windows 子系统（WSL）允许开发人员直接在Windows 上运行GNU/Linux 环境（包括大多数命令行工具、实用工具和应用程序），无需传统虚拟机或双启动设置的开销。\n\n### 1. 安装wsl ubuntn24.04\n访问：https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#downloading-distributions\n\n下载Ubuntu24.04\n\n双击Ubuntu2404-240425.AppxBundle 安装Ubuntu\n\n\n### 2. 进入wsl，配置代理\n窗口输入wsl进入ubuntu命令行\n\n- cmd\n\n- wsl\n\n切换到root用户\n\n- sudo su\n\n配置翻墙代理，如果你有\n\n- export http_proxy=\"http://10.115.164.50:8080\"\n- export https_proxy=\"http://10.115.164.50:8080\"\n\n### 3. 安装python3.8和frida\n- apt update\n- apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev \nlibreadline-dev libsqlite3-dev wget curl llvm xz-utils tk-dev \nlibffi-dev liblzma-dev\n- apt install -y git\n- apt install -y pyenv\n- pyenv install 3.8.8\n- pyenv local 3.8\n\n\n# 自定义frida-server\n- 将您自定义的frida-server文件拷贝到mobile-deploy文件夹下\n- 修改hooker.py，default_frida_server_arm和default_frida_server_arm64变量的名字为你自定义的文件名\n\n```python\ndefault_frida_server_arm = \"your-custom-frida-server-android-arm\"\ndefault_frida_server_arm64 = \"your-custom-frida-server-android-arm64\"\n```\n\n# hooker命令行快捷键\n\n- Ctrl + U：整行清空\n\n- Ctrl + W：删除一个单词\n\n- Ctrl + K：从光标删到行尾\n"
  },
  {
    "path": "README_EN.md",
    "content": "⚠️ Disclaimer\nAll 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.\n\nThis project does not contain any cracking operations or infringing content targeting specific applications.\nSome simple unpacking techniques related to certain apps are widely available on major technical forums and do not cause actual harm to the applications.\nThis 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.\nPlease 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.\n\n<p>English | <a href=\"README.md\">简体中文</a></p>\n\n# 👋 Welcome to Hooker Reverse Engineering Toolkit\n![GitHub stars](https://img.shields.io/github/stars/CreditTone/hooker?style=flat-square)\n![GitHub forks](https://img.shields.io/github/forks/CreditTone/hooker?style=flat-square)\n![GitHub code size](https://img.shields.io/github/languages/code-size/CreditTone/hooker?style=flat-square)\n![Python](https://img.shields.io/badge/python-3.8.8-blue?style=flat-square)\n![frida](https://img.shields.io/badge/frida-16.7.19-blue?style=flat-square)\n\nHooker is a reverse engineering toolkit based on Frida, designed to provide Android reverse engineers with a comfortable command-line interface,\n\nA unified script package management system\n\nUniversal (通杀) scripts\n\nAutomated hook script generation\n\nIn-memory roaming for detecting Activity and Service components\n\nA Frida-based implementation of JustTrustMe\n\nGlobal app support for boringssl unpinning\n\nOne picture to prove why you need Hooker:\n![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg)\n\n- 1. A Frida-based implementation of JustTrustMe for universal SSL pinning bypass, with ongoing maintenance.\n- 2. An embedded webserver that can quickly expose in-app capabilities as HTTP endpoints for automation and API-style workflows.\n- 3. Automated Frida hook script generation with detailed, extensible templates.\n- 4. One-click SOCKS5 proxy setup without requiring third-party apps such as SocksDroid.\n- 5. A highly streamlined command-line workflow that makes daily reversing much more comfortable.\n\n## 📚 Table of Contents\n\n- [Quick Start](#quick-start)\n  - [1. Clone the repository](#1-clone-the-repository)\n  - [2. Install Python dependencies](#2-install-python-dependencies)\n  - [3. Connect your rooted device via USB](#3-connect-your-rooted-device-via-usb)\n  - [4. Launch Hooker](#4-launch-hooker)\n  - [5. Enter the package name of the target app](#5-enter-the-package-name-of-the-target-app)\n  - [6. View help information](#6-view-help-information)\n  - [7. Embedded webserver](#7-embedded-webserver)\n  - [8. Generate Frida hook scripts for a specific class and method](#8-generate-frida-hook-scripts-for-a-specific-class-and-method)\n  - [9. List all available Frida scripts](#9-list-all-available-frida-scripts)\n  - [10. Attach and execute a specific Frida script](#10-attach-and-execute-a-specific-frida-script)\n  - [11. Set a SOCKS5 proxy for the app](#11-set-a-socks5-proxy-for-the-app)\n  - [12. Run JustTrustMe to disable all SSL pinning (including boringssl)](#12-run-justtrustme-to-disable-all-ssl-pinning-including-boringssl)\n  - [13. Spawn the app and execute a specific Frida script](#13-spawn-the-app-and-execute-a-specific-frida-script)\n  - [14. Clear the proxy settings](#14-clear-the-proxy-settings)\n  - [15. Restart the app](#15-restart-the-app)\n  - [16. Get the app's UID and PID](#16-get-uid-and-pid)\n\n\n# 🚀 Quick Start\n\nEnsure your device is rooted. No need to manually start frida-server or do any configuration—Hooker handles everything for you.\n\n\n### 1. Clone the repository\n```shell\nstephen@ubuntu:~$ git clone https://github.com/CreditTone/hooker.git\nstephen@ubuntu:~$ cd hooker\n```\n\n### 2. Install Python dependencies\n```shell\nstephen@ubuntu:~/hooker$ pip3 install -r requirements.txt\n```\n\n\n### 3. Connect your rooted device via USB\n```shell\nstephen@ubuntu:~/hooker$ adb devices\nList of devices attached\nFA77C0301476\tdevice\n```\n\n\n### 4. Launch Hooker\n```shell\nstephen@ubuntu:~/hooker$ python3 hooker.py\nhooker Let's enjoy reverse engineering together\n-----------------------------------------------------------------------------------------------\nPID   \tAPP                 \tIDENTIFIER                         \tEXIST_REVERSE_DIRECTORY\n0     \t全球上网            \tcom.miui.virtualsim                \t❌\n0     \t爱奇艺              \tcom.qiyi.video                     \t❌\n0     \t红手指云手机        \tcom.redfinger.app                  \t❌\n0     \tReqable             \tcom.reqable.android                \t❌\n0     \t美团                \tcom.sankuai.meituan                \t✅\n0     \t得物                \tcom.shizhuang.duapp                \t❌\n0     \t某皮           \t     cxm.shxpxx.sg                      \t✅\n0     \t微博                \tcom.sina.weibo                     \t❌\n0     \t今日头条            \tcom.ss.android.article.news        \t✅\n0     \t西瓜视频            \tcom.ss.android.article.video       \t✅\n0     \t懂车帝              \tcom.ss.android.auto                \t✅\n0     \t抖音火山版          \tcom.ss.android.ugc.live            \t✅\n0     \t抖音精选            \tcom.ss.android.yumme.video         \t❌\n0     \t淘宝                \tcom.taobao.taobao                  \t✅\n0     \t腾讯视频            \tcom.tencent.qqlive                 \t❌\n0     \tTermux              \tcom.termux                         \t❌\n0     \t轻奢                \tcom.tm.bachelorparty               \t✅\n0     \tWiFi ADB            \tcom.ttxapps.wifiadb                \t❌\n0     \tVMOS Pro            \tcom.vmos.pro                       \t✅\n0     \t游戏中心            \tcom.xiaomi.gamecenter              \t❌\n0     \t小米商城            \tcom.xiaomi.shop                    \t❌\n0     \t米家                \tcom.xiaomi.smarthome               \t❌\n0     \t小米有品            \tcom.xiaomi.youpin                  \t✅\n0     \t小红书              \tcom.xingin.xhs                     \t✅\n0     \t运满满货主          \tcom.xiwei.logistics.consignor      \t✅\n0     \t拼多多              \tcom.xunmeng.pinduoduo              \t✅\n0     \tEnvCheck            \tcom.yimian.envcheck                \t✅\n0     \tcheck_env           \tcom.yuuki.check_env                \t❌\n0     \tTikTok              \tcom.zhiliaoapp.musically           \t❌\n0     \tXPrivacyLua         \teu.faircode.xlua                   \t❌\n0     \timToken             \tim.token.app                       \t❌\n0     \tSocksDroid          \tnet.typeblog.socks                 \t❌\n0     \tF-Droid             \torg.fdroid.fdroid                  \t❌\n0     \tProxyDroid          \torg.proxydroid                     \t❌\n3457  \t手机管家            \tcom.miui.securitycenter            \t✅\n3509  \t优信拍              \tcom.uxin.buyerphone                \t✅\n18780 \t抖音                \tcom.ss.android.ugc.aweme           \t✅\n20174 \t应用商店            \tcom.xiaomi.market                  \t❌\n20913 \t设置                \tcom.android.settings               \t❌\n30500 \t小爱同学            \tcom.miui.voiceassist               \t❌\n32163 \t相机                \tcom.android.camera                 \t✅\nPlease enter the identifier that needs to be reversed\nhooker(Identifier):\n```\n***\n\n### 5. Enter the package name of the target app\n```shell\nhooker(Identifier): cxm.shxpxx.sg\n✅ App cxm.shxpxx.sg is already in the foreground\nCreating working directory: cxm.shxpxx.sg\nGenerating frida shortcut command...\nGenerating built-in frida script...\npull /data/app/cxm.shxpxx.sg-L8zkrpFVICv0-hOrtmPPxA==/base.apk to cxm.shxpxx.sg/ShopeeSG_3.43.40.apk successful\nWorking directory create successful\njust_trust_me.js                                 empty.js                                         keystore_dump.js\nedit_text.js                                     activity_events.js                               find_boringssl_custom_verify_func.js\nssl_log.js                                       hook_register_natives.js                         click.js\nget_device_info.js                               apk_shell_scanner.js                             dump_dex.js\nobject_store.js                                  hook_artmethod_register.js                       replace_dlsym_get_pthread_create.js\njust_trust_me_for_ios.js                         trace_initproc.js                                android_ui.js\nhook_jni_method_trace.js                         url.js                                           just_trust_me_okhttp_hook_finder_for_android.js\ntext_view.js                                     find_anit_frida_so.js\n某皮 > \n```\n![hooker_enter_debug.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_enter_debug.gif)\n***\n\n\n\n\n### 6. View help information\n\n```shell\n某皮 > help\nh, help                                      show this help message\na, activitys                                 show the activity stack\ns, services                                  show the service stack\no, object [object_id]                        show object info by object_id\nv, view [view_id]                            show view info by view_id of view\ngs, generatescript [class_name:method_name]  specify the class name and method name to generate a frida hook java script file. For example: generatescript\n                                             okhttp3.Request$Builder:addHeader\np, proxy [socks5_proxy_server]               set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998\nup, unproxy                                  remove socks5 proxy for this app\ntrust, justtrustme                           quickly spawn just_trust_me.js script to kill all ssl pinning\nls                                           list all the frida scripts of the current app\nattach [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\nspawn [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\n                                             just_trust_me.js\nrestart                                      restart this app\npid                                          get pid of this app main process\nuid                                          get pid of this app\nexit                                         return to the previous level\n某皮 > \n```\n![hooker_help.gif](https://raw.githubusercontent.com/CreditTone/img_resources/main/hooker_help.gif)\n***\n\n\n### 7. Embedded webserver\n\nHooker 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.\n\n- Start the built-in webserver\n\n```shell\n某音火山版 > webserver start\nHttp server port: 8080\nHttp server: http://10.112.101.249:8080\n```\n\nAfter the built-in webserver starts, open the root page in a browser to see all registered APIs. Common built-in capabilities include:\n\n- Service management: `/` shows the welcome page and API list, and `/stop` stops the current webserver.\n- 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.\n- 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.\n- Screenshot capture: `/hooker/screencap/screenshot` uses the system `screencap` command, while `/hooker/mediaprojection/...` supports MediaProjection permission flow and full-screen PNG capture.\n- 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.\n- 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.\n- File serving: `/file?filename=...` returns an absolute-path file or a file generated in the webserver cache directory.\n- 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.\n\n- Start a custom webserver\n\n```shell\n某宝 > webserver start taxbax-patch.jar\nConverting taxbax-patch.jar to taxbax-patch.dex...\nSuccessfully converted to taxbax-patch.dex (7160 bytes)\npush file OK /data/user/0/com.taxbax.taxbax/hooker_server.dex\nHttp server port: 2026\nHttp server: http://10.112.101.249:2026\n```\n\nHere `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.\n\nCustom webservers are useful for:\n\n- Exposing app-internal business capabilities such as search, comments, product details, signatures, encryption/decryption, user profiles, or live-stream APIs.\n- Reusing the target app's own login state, networking stack, environment values, and in-memory objects instead of rebuilding protocol details externally.\n- Wrapping asynchronous callbacks, observables, listeners, or page-object calls into synchronous HTTP endpoints that return JSON or plain text.\n\nPatch projects typically define endpoints like this:\n\n- Use `@HookerWebServerConfiguration(port = 2026)` to specify the port. If omitted, the default is `8080`.\n- Use `@HookerController(\"/taobao\")` or `@HookerController(\"/douyin\")` to define the route prefix.\n- Use `@HookerRequestMapping(path = \"/getProductDetail\")` to expose concrete endpoints.\n- Use `@HookerRequestParam` and `@HookerRequestPostJson` to receive query parameters and POST JSON.\n\nThis 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.\n\n- Webserver persistence\n\nIf 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`:\n\n`https://github.com/CreditTone/HookerServer`\n\nIf the device supports Xposed/LSPosed, you can use this plugin to persist the webserver inside the target app.\n\nSteps:\n\n1. Download the latest APK from `https://github.com/CreditTone/HookerServer/releases`.\n2. Push `patch.dex` to `/data/user/0/{package}/hooker_server.dex`.\n3. Enable `HookerServer` for the target app in Xposed/LSPosed.\n4. Restart the app, and the webserver will start automatically.\n\nThe `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.\n***\n\n\n### 8. Generate Frida hook scripts for a specific class and method\n\n![gs_show.jpg](https://raw.githubusercontent.com/CreditTone/img_resources/main/gs_show.jpg)\n\n- Command Syntax：gs, generatescript [class_name:method_name]\n\n\n- 8.1 Generate a Frida hook script for a specific method:\ngs okhttp3.Request$Builder:addHeader — the parameter part (String, String) is not required.\n\n```shell\n某信拍 > gs okhttp3.Request$Builder:addHeader(String, String)\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder.addHeader.js\n某信拍 > \n```\n\n```js\n//cat okhttp3.Request.Builder.addHeader.js\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_method_addHeader_2grl = okhttp3_Request_Builder_clz.addHeader.overload('java.lang.String', 'java.lang.String');\n    okhttp3_Request_Builder_clz_method_addHeader_2grl.implementation = function(string, string_x2) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.addHeader(java.lang.String,java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_addHeader_2grl.call(this, string, string_x2);\n        console.log(\"header name:\" + string + \" header value:\" + string_x2);\n        printBeat(beat);\n        return ret;\n    };\n});\n```\n***\n\n\n\n- 8.2 Generate a Frida hook script for all member methods of a specified class:\ngs okhttp3.Request$Builder\n\n```shell\n某信拍 > generatescript okhttp3.Request$Builder\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder.allfunc.js\n```\n***\n\n```js\n//cat okhttp3.Request.Builder.allfunc.js\n//okhttp3.Request$Builder\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_method_header_ng3n = okhttp3_Request_Builder_clz.header.overload('java.lang.String', 'java.lang.String');\n    okhttp3_Request_Builder_clz_method_header_ng3n.implementation = function(string, string_x2) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.header(java.lang.String,java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_header_ng3n.call(this, string, string_x2);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_cacheControl_q8q5 = okhttp3_Request_Builder_clz.cacheControl.overload('okhttp3.CacheControl');\n    okhttp3_Request_Builder_clz_method_cacheControl_q8q5.implementation = function(cacheControl) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.cacheControl(okhttp3.CacheControl)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_cacheControl_q8q5.call(this, cacheControl);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_method_bjk9 = okhttp3_Request_Builder_clz.method.overload('java.lang.String', 'okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_method_bjk9.implementation = function(string, requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.method(java.lang.String,okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_method_bjk9.call(this, string, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_head_a5nq = okhttp3_Request_Builder_clz.head.overload();\n    okhttp3_Request_Builder_clz_method_head_a5nq.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.head()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_head_a5nq.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_headers_to5i = okhttp3_Request_Builder_clz.headers.overload('okhttp3.Headers');\n    okhttp3_Request_Builder_clz_method_headers_to5i.implementation = function(headers) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.headers(okhttp3.Headers)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_headers_to5i.call(this, headers);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_post_heaq = okhttp3_Request_Builder_clz.post.overload('okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_post_heaq.implementation = function(requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.post(okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_post_heaq.call(this, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_build_rmqx = okhttp3_Request_Builder_clz.build.overload();\n    okhttp3_Request_Builder_clz_method_build_rmqx.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request okhttp3.Request$Builder.build()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_build_rmqx.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_patch_hp9u = okhttp3_Request_Builder_clz.patch.overload('okhttp3.RequestBody');\n    okhttp3_Request_Builder_clz_method_patch_hp9u.implementation = function(requestBody) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.patch(okhttp3.RequestBody)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_patch_hp9u.call(this, requestBody);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_url_0owi = okhttp3_Request_Builder_clz.url.overload('java.lang.String');\n    okhttp3_Request_Builder_clz_method_url_0owi.implementation = function(string) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_url_0owi.call(this, string);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_removeHeader_uzb9 = okhttp3_Request_Builder_clz.removeHeader.overload('java.lang.String');\n    okhttp3_Request_Builder_clz_method_removeHeader_uzb9.implementation = function(string) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.removeHeader(java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_removeHeader_uzb9.call(this, string);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_url_ykbd = okhttp3_Request_Builder_clz.url.overload('java.net.URL');\n    okhttp3_Request_Builder_clz_method_url_ykbd.implementation = function(url) {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.url(java.net.URL)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_url_ykbd.call(this, url);\n        printBeat(beat);\n        return ret;\n    };\n    var okhttp3_Request_Builder_clz_method_delete_dqyl = okhttp3_Request_Builder_clz.delete.overload();\n    okhttp3_Request_Builder_clz_method_delete_dqyl.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder okhttp3.Request$Builder.delete()';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = okhttp3_Request_Builder_clz_method_delete_dqyl.call(this);\n        printBeat(beat);\n        return ret;\n    };\n    //.......省略N行代码\n```\n***\n\n\n\n- 8.3 Generate a Frida hook script for the constructor(s) of a specified class:\ngs okhttp3.Request$Builder:_ or gs okhttp3.Request$Builder:\\<init\\>\n\n```shell\n某信拍 > gs okhttp3.Request$Builder:<init>()\nGenerating frida script, please wait for a few seconds\nfrida hook script: okhttp3.Request.Builder._init.js\n```\n\n```js\n//cat okhttp3.Request.Builder._init.js\n//okhttp3.Request$Builder:<init>()\nJava.perform(function() {\n    var okhttp3_Request_Builder_clz = Java.use('okhttp3.Request$Builder');\n    var okhttp3_Request_Builder_clz_init_uw3i = okhttp3_Request_Builder_clz.$init.overload();\n    okhttp3_Request_Builder_clz_init_uw3i.implementation = function() {\n        var executor = this.hashCode();\n        var beatText = 'public okhttp3.Request$Builder()';\n        var beat = newMethodBeat(beatText, executor);\n        var returnObj = okhttp3_Request_Builder_clz_init_uw3i.call(this);\n        printBeat(beat);\n        return returnObj;\n    };\n    var okhttp3_Request_Builder_clz_init_e58t = okhttp3_Request_Builder_clz.$init.overload('okhttp3.Request');\n    okhttp3_Request_Builder_clz_init_e58t.implementation = function(v0) {\n        var executor = this.hashCode();\n        var beatText = 'okhttp3.Request$Builder(okhttp3.Request)';\n        var beat = newMethodBeat(beatText, executor);\n        var returnObj = okhttp3_Request_Builder_clz_init_e58t.call(this, v0);\n        printBeat(beat);\n        return returnObj;\n    };\n});\n```\n***\n\n### 9. List all available Frida scripts\n```shell\n某皮 > ls\njust_trust_me.js                                 empty.js                                         keystore_dump.js\nokhttp3.Request.Builder.addHeader.js             edit_text.js                                     activity_events.js\nfind_boringssl_custom_verify_func.js             ssl_log.js                                       hook_register_natives.js\nclick.js                                         get_device_info.js                               apk_shell_scanner.js\ndump_dex.js                                      object_store.js                                  hook_artmethod_register.js\nreplace_dlsym_get_pthread_create.js              just_trust_me_for_ios.js                         trace_initproc.js\nandroid_ui.js                                    hook_jni_method_trace.js                         url.js\njust_trust_me_okhttp_hook_finder_for_android.js  text_view.js                                     find_anit_frida_so.js\n某皮 >\n```\n***\n\n### 10. Attach and execute a specific Frida script\n\n> **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.\n\n```shell\n某信拍 > attach url.js\nFrida output logging -> com.uxin.buyerphone/url.log\n------------startFlag:0755liv1,objectHash:-915348569,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836814835---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836814832&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\npublic java.net.URL(String)\n\tat java.net.URL.<init>(Native Method)\n\tat com.wxbx.wmda.e.b.a(SourceFile:5)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:0755liv1,usedtime:1---------------\n\n------------startFlag:1ps6go99,objectHash:-237375819,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815192---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\npublic java.net.URL(String)\n\tat java.net.URL.<init>(Native Method)\n\tat com.android.okhttp.HttpUrl.url(HttpUrl.java:327)\n\tat com.android.okhttp.Request.url(Request.java:53)\n\tat com.android.okhttp.Request$Builder.build(Native Method)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258)\n\tat com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)\n\tat com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26)\n\tat com.wxbx.wmda.e.b.a(SourceFile:14)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:1ps6go99,usedtime:0---------------\n// 这里省略无数日志.............\n------------startFlag:i7osxvjl,objectHash:134280600,thread(id:810,name:Wmda.EventUploadThread),timestamp:1747836815193---------------\nurl:https://apiwmxx.xxx.com.cn/report/c?api_v=3&sdk_v=1.7.0.0&timestamp=1747836815188&appid=17591177894321&p=2&uuid=248056262e0030b7bb56c0f9237f846d\ncom.android.okhttp.Request.Builder.build()\n\tat com.android.okhttp.Request$Builder.build(Native Method)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.newHttpEngine(HttpURLConnectionImpl.java:377)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.initHttpEngine(HttpURLConnectionImpl.java:332)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:124)\n\tat com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:258)\n\tat com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)\n\tat com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:26)\n\tat com.wxbx.wmda.e.b.a(SourceFile:14)\n\tat com.wxbx.wmda.e.b.a(SourceFile:1)\n\tat com.wxbx.wmda.h.a.a(SourceFile:162)\n\tat com.wxbx.wmda.h.a.b(SourceFile:19)\n\tat com.wxbx.wmda.h.a.a(SourceFile:2)\n\tat com.wxbx.wmda.h.a$b.handleMessage(SourceFile:3)\n\tat android.os.Handler.dispatchMessage(Handler.java:106)\n\tat android.os.Looper.loop(Looper.java:201)\n\tat android.os.HandlerThread.run(HandlerThread.java:65)\n------------endFlag:i7osxvjl,usedtime:1---------------\n// 这里省略无数日志.............\n```\n***\n\n### 11. Set a SOCKS5 proxy for the app\n```shell\n某音 > proxy socks5://10.112.99.11:9998\nproxy socks5://10.112.99.11:9998 OK\n某音 > \n```\n***\n\n\n### 12. Run JustTrustMe to disable all SSL pinning (including boringssl)\n\n```shell\n某音 > justtrustme\nPackage name: com.ss.xxxx.xxx.aweme\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\nandroid.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\nstatic 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!\nstatic 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!\nstatic 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!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nstatic 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!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nstatic 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!\nokhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\nokhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!\n// 这里省略无数日志.............\n```\n***\n\n\n### 13. Spawn the app and execute a specific Frida script\n\n> **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`.\n\n```shell\n某信拍 > spawn just_trust_me.js\nFrida output logging -> com.uxin.buyerphone/just_trust_me.log\nPackage name: com.xxx.buyxxphone\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\njavax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!\njavax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!\n// 这里省略无数日志.............\n```\n***\n\n\n### 14. Clear the proxy settings\n```shell\n某音 > unproxy\nunproxy OK\n某音 > \n```\n***\n\n\n\n### 15. Restart the app\n\n```shell\n某信拍 > restart\nrestarts com.xxx.buyxxphone\n```\n***\n\n\n### 16. Get UID and PID\n```shell\n某信拍 > uid\n10189\n某信拍 > pid\n3509\n```\n***\n"
  },
  {
    "path": "hooker.py",
    "content": "#!/usr/bin/env python3\n\n'''\nCreated on 2020年3月23日\n\n@author: stephen\n'''\n\ndefault_frida_server_arm = \"frida-server-16.7.19-android-arm\"\ndefault_frida_server_arm64 = \"frida-server-16.7.19-android-arm64\"\n\n\nimport frida, sys\nimport os\nimport io\nimport re\nimport stat\nimport time\nimport json\nimport getopt\nimport traceback\nimport base64\nimport time\nimport platform\nimport threading\nimport adbutils\nimport hashlib\nimport shutil\nimport textwrap\nimport zipfile\nimport queue\nimport sqlite3\nimport itertools\nimport jsbeautifier\nimport logging\nimport subprocess\nimport filecmp\nimport argparse\nimport pprint\nimport random\nimport signal\nimport socket\nimport struct\nimport binascii\nfrom git import Repo\nfrom pathlib import Path\nfrom loguru import logger\nfrom datetime import datetime\nfrom collections import Counter\nfrom dataclasses import dataclass\nfrom androguard.core import androconf\nfrom androguard.core.bytecodes import apk\nfrom androguard.core.bytecodes import dvm\nfrom androguard.core.analysis.analysis import MethodAnalysis\n\nfrom typing import Optional, Tuple, List, Dict\nfrom adbutils.errors import AdbError\nfrom prompt_toolkit import PromptSession\nfrom prompt_toolkit.completion import Completer, Completion\nfrom prompt_toolkit.completion import WordCompleter\nfrom prompt_toolkit.completion import NestedCompleter\nfrom prompt_toolkit.patch_stdout import patch_stdout\nfrom wcwidth import wcswidth\n\n\ndef find_android_home() -> Optional[str]:\n    \"\"\"\n    在 macOS/Linux 上查找 Android SDK 路径\n\n    Returns:\n        Android SDK 路径，如果未找到则返回 None\n    \"\"\"\n    # 1. 首先检查环境变量\n    for env_var in ['ANDROID_HOME', 'ANDROID_SDK_ROOT']:\n        sdk_path = os.environ.get(env_var)\n        if sdk_path and os.path.isdir(sdk_path):\n            return os.path.abspath(sdk_path)\n\n    # 2. 检查操作系统特定的默认安装路径\n    if sys.platform == 'darwin':  # macOS\n        default_paths = [\n            os.path.join(os.path.expanduser('~'), 'Library', 'Android', 'sdk'),\n            '/usr/local/share/android-sdk',\n            '/opt/homebrew/share/android-sdk',\n            '/opt/android-sdk',\n            '/usr/local/opt/android-sdk',\n        ]\n    else:  # Linux\n        default_paths = [\n            os.path.join(os.path.expanduser('~'), 'Android', 'Sdk'),\n            '/usr/lib/android-sdk',\n            '/opt/android-sdk',\n            '/usr/local/android-sdk',\n            '/opt/local/android-sdk',\n        ]\n\n    # 检查默认路径\n    for path in default_paths:\n        if path and os.path.isdir(path):\n            # 验证是否是有效的 Android SDK 目录\n            if os.path.exists(os.path.join(path, 'build-tools')):\n                return os.path.abspath(path)\n\n    return None\n\n\ndef get_oldest_dx_d8_path(android_home: Optional[str] = None, min_version: str = \"30.0.0\") -> Tuple[\n    Optional[str], Optional[str]]:\n    \"\"\"\n    查找最老的 dx 和最老的 d8 编译器路径，但版本号不低于 min_version\n\n    Args:\n        android_home: Android SDK 路径，如果为 None 则自动查找\n        min_version: 最小版本号，默认 30.0.0\n\n    Returns:\n        (最老的d8文件路径, 最老的dx文件路径)，如果未找到则返回 (None, None)\n    \"\"\"\n    # 1. 获取 Android SDK 路径\n    if android_home is None:\n        android_home = find_android_home()\n\n    if not android_home:\n        return None, None\n\n    # 2. 检查 build-tools 目录\n    build_tools_dir = os.path.join(android_home, 'build-tools')\n    if not os.path.isdir(build_tools_dir):\n        return None, None\n\n    # 3. 解析最小版本号\n    def parse_version(version_str: str) -> List[int]:\n        \"\"\"将版本号字符串转换为整数列表便于比较\"\"\"\n        parts = version_str.split('.')\n        result = []\n        for part in parts:\n            try:\n                result.append(int(part))\n            except ValueError:\n                # 如果包含非数字部分，只取数字部分\n                num_part = re.search(r'^\\d+', part)\n                if num_part:\n                    result.append(int(num_part.group()))\n                else:\n                    result.append(0)\n        # 补齐到3位\n        while len(result) < 3:\n            result.append(0)\n        return result\n\n    min_version_parts = parse_version(min_version)\n\n    # 4. 遍历所有 build-tools 版本，分别收集满足条件的 dx 和 d8 文件\n    d8_files = []\n    dx_files = []\n\n    # 获取所有版本目录\n    for item in os.listdir(build_tools_dir):\n        version_dir = os.path.join(build_tools_dir, item)\n        if not os.path.isdir(version_dir):\n            continue\n\n        # 检查是否是有效的版本号格式（如 34.0.0）\n        if not re.match(r'^\\d+(\\.\\d+)*$', item):\n            continue\n\n        # 检查版本号是否 >= 30.0.0\n        version_parts = parse_version(item)\n\n        # 版本比较函数\n        def version_gte(v1, v2):\n            \"\"\"比较版本 v1 是否大于等于 v2\"\"\"\n            for part1, part2 in zip(v1, v2):\n                if part1 > part2:\n                    return True\n                elif part1 < part2:\n                    return False\n            return True  # 完全相等\n\n        # 如果版本号小于最小版本，跳过\n        if not version_gte(version_parts, min_version_parts):\n            continue\n\n        # 查找 dx 和 d8 文件\n        dx_path = os.path.join(version_dir, 'dx')\n        d8_path = os.path.join(version_dir, 'd8')\n\n        # 分别收集 d8 和 dx 文件\n        if os.path.isfile(d8_path) and os.access(d8_path, os.X_OK):\n            d8_files.append((item, d8_path))\n\n        if os.path.isfile(dx_path) and os.access(dx_path, os.X_OK):\n            dx_files.append((item, dx_path))\n\n    # 5. 获取最老的 d8 和 dx（在>=30.0.0的版本中）\n    oldest_d8_path = None\n    oldest_dx_path = None\n\n    if d8_files:\n        # 按版本号升序排列（最老的在前）\n        d8_files.sort(key=lambda x: parse_version(x[0]))\n        oldest_d8_path = d8_files[0][1]\n\n    if dx_files:\n        # 按版本号升序排列（最老的在前）\n        dx_files.sort(key=lambda x: parse_version(x[0]))\n        oldest_dx_path = dx_files[0][1]\n\n    return oldest_d8_path, oldest_dx_path\n\n\ndef withColor(string, fg, bg=49):\n    print(\"\\33[0m\\33[%d;%dm%s\\33[0m\" % (fg, bg, string))\n#front color\nRed = 1\nGreen = 2\nYellow = 3\nBlue = 4\nMagenta = 5\nCyan = 6\nWhite = 7\n\ndef red(string):\n    return withColor(string, Red+30) # Red\ndef green(string):\n    return withColor(string, Green+30) # Green\ndef yellow(string):\n    return withColor(string, Yellow+30) # Yellow\ndef blue(string):\n    return withColor(string, Blue+30) # Blue\ndef magenta(string):\n    return withColor(string, Magenta+30) # Magenta\ndef cyan(string):\n    return withColor(string, Cyan+30) # Cyan\ndef white(string):\n    return withColor(string, White+30) # White\n#日志打印颜色定义\nwarn = red\ninfo = yellow\n\ndef print_js_file(filenames :list):\n    if not filenames:\n        return\n    GREEN = \"\\033[32m\"\n    RESET = \"\\033[0m\"\n    columns, _ = shutil.get_terminal_size()\n    # 计算每个字段宽度\n    max_len = max(len(name) for name in filenames) + 2\n    items_per_line = max(1, columns // max_len)\n    # 输出带颜色的文件名\n    for i in range(0, len(filenames), items_per_line):\n        line = \"\".join(f\"{GREEN}{name.ljust(max_len)}{RESET}\" for name in filenames[i:i + items_per_line])\n        print(line)\n\ndef read_js_resource(filename):\n    return io.open('./js/' + filename,'r',encoding= 'utf8').read()\n        \ncmd_session = None\n\n\nadb_device = None\n\ndef _init_adb_device():\n    global adb_device\n    adb_device = adbutils.adb.device()\n\n_init_adb_device()\n\ndef _shell(cmd, stream=False):\n    return adb_device.shell(cmd, stream=stream)\n\ndef run_su_command(cmd, not_read=False):\n    try:\n        if not_read:\n            _shell(f\"{cmd} > /dev/null 2>&1 &\")\n            time.sleep(1)\n            return\n        output = _shell(cmd).strip()\n        if output:\n            return output\n    except Exception:\n        pass\n    conn = _shell([\"su\", \"-c\", cmd], stream=True)\n    try:\n        if not_read:\n            time.sleep(1)\n            return\n        output = conn.read_until_close()\n        return output.strip()\n    finally:\n        try:\n            conn.close()\n        except Exception as e:\n            pass\n\n\ndef get_is_magisk_root() -> bool:\n    out = run_su_command(\"su -c id\")\n    if \"uid=0\" not in out:\n        return False\n    # 有些设备不一定带 context 字段，但你这台带了，带了就几乎 100% 是 Magisk\n    if \"context=u:r:magisk:s0\" in out:\n        return True\n    # 没有 context 也可能是 root（或别的 su），再加个 Magisk 文件特征判断更保险\n    magisk_marker = run_su_command(\"ls /data/adb/magisk.db 2>/dev/null\")\n    return bool(magisk_marker)\n\nis_magisk_root = get_is_magisk_root()\n\n\n#初始化frida运行环境\ndef is_frida_working_via_attach(target_package=\"com.android.systemui\"):\n    try:\n        __device = frida.get_usb_device(timeout=3)  # or use add_remote_device(ip)\n        pid = __device.get_process(target_package).pid  # 先确认包是否存在\n        _session = __device.attach(pid)\n        _session.detach()\n        return True\n    except frida.ServerNotRunningError:\n        #info(\"其他异常: ServerNotRunningError\")\n        return False\n    except frida.ProcessNotFoundError:\n        #info(\"⚠️ 找不到进程，说明包名可能错误，但 frida 正常\")\n        return True  # 仍然说明 frida-server 已连通\n    except frida.TimedOutError:\n        #info(\"❌ 连接超时，frida-server 可能未运行或设备未连接\")\n        return False\n    except Exception as e:\n        #info(f\"其他异常: {e}\")\n        return False\n\ndef check_remote_file_exists(path):\n    result = adb_device.shell(f\"test -f {path} && echo exists || echo missing\")\n    return result.strip() == \"exists\"\n\ndef check_remote_dir_exists(path):\n    result = adb_device.shell(f\"[ -d {path} ] && echo exists || echo not_exists\")\n    return result.strip() == \"exists\"\n\ndef get_cpu_arch():\n    abi = adb_device.shell(\"getprop ro.product.cpu.abi\").strip()\n    if \"arm64\" in abi:\n        return \"arm64\"\n    elif \"armeabi\" in abi:\n        return \"arm\"\n    elif \"x86_64\" in abi:\n        return \"x86_64\"\n    elif \"x86\" in abi:\n        return \"x86\"\n    return \"arm64\"\n\ndef choose_frida_server():\n    cpu_arch = get_cpu_arch()\n    if \"arm64\" == cpu_arch:\n        return default_frida_server_arm64\n    elif \"arm\" == cpu_arch:\n        return default_frida_server_arm\n    info(\"For simulator, please start frida-server manually first. Thank you\")\n    sys.exit(2)\n    \ndef pull_file_to_local(remote_file, local_path, is_debug=True):\n    adb_device.sync.pull(remote_file, local_path)\n    if is_debug:\n        info(f\"pull {remote_file} to {local_path} successful\")\n        \ndef push_file_to_remote(local_path, remote_path, is_debug=True):\n    # info(f\"push {local_path} to {remote_path}\")\n    from adbutils.errors import AdbError\n    try:\n        # 先尝试标准推送\n        adb_device.sync.push(local_path, remote_path)\n    except AdbError as e:\n        #info(\"检测到API兼容性问题，降级到更基本的adb push命令\")\n        # 降级到更基本的adb push命令\n        subprocess.run(\n            [\"adb\", \"push\", local_path, remote_path],\n            check=True\n        )\n    if is_debug:\n        info(f\"push {local_path} to {remote_path} successful\")\n    \ndef is_root():\n    output = run_su_command(\"ls /data/\")\n    return \"cache\" in output and \"user\" in output\n\ndef ensure_root():\n    if is_root():\n        return True\n    else:\n        try:\n            adb_device.root()  # adbutils 内置封装\n            if is_root():\n                info(\"Switched to root successfully ✅\")\n                return True\n            else:\n                info(\"❌ Device is not rooted\")\n                return False\n        except Exception as e:\n            info(f\"❌ Failed to switch to root: {e}\")\n            return False\n\n\n# 自动化部署frida-server    \nif not is_frida_working_via_attach():\n    if not ensure_root():\n        info(\"❌ Cannot deploy frida-server automatically. Please start frida-server manually and try again.\")\n        sys.exit(2)\n    frida_server_file = choose_frida_server()\n    remote_frida_server_file = f\"/data/mobile-deploy/{frida_server_file}\"\n    if not check_remote_dir_exists(\"/data/mobile-deploy/\"):\n        run_su_command(\"mkdir /data/mobile-deploy/\")\n    if not check_remote_file_exists(remote_frida_server_file):\n        push_file_to_remote(f\"mobile-deploy/{frida_server_file}\", \"/sdcard/\")\n        run_su_command(f\"mv /sdcard/{frida_server_file} {remote_frida_server_file}\")\n        run_su_command(f\"chmod +x {remote_frida_server_file}\")\n    run_su_command(\"setenforce 0\")\n    run_su_command(f\"/data/mobile-deploy/{choose_frida_server()} -D > /sdcard/f_server.log 2>&1\", False)\n    success = False\n    for index in range(20):\n        if is_frida_working_via_attach():\n            info(\"frida-server started successfully ✅\")\n            success = True\n            break\n        time.sleep(0.5)\n    if not success:\n        info(\"❌ Failed to start frida-server automatically. Please start it manually and try again.\")\n        sys.exit(2)\n\n# 全局变量        \ncurrent_identifier = None\ncurrent_identifier_name = None\ncurrent_identifier_version = None\ncurrent_identifier_pid = None\ncurrent_identifier_install_path = None\ncurrent_identifier_install_apkfilename = None\ncurrent_identifier_uid = None\ncurrent_local_apk_path = None\ncurrent_identifier_cache_db = None\ncurrent_identifier_cache_readonly_db = None\ncurrent_identifier_stop_event = None\nwebserver_url = None\n\nfrida_device = None\n\nresource_rpc_jscode = read_js_resource(\"rpc.js\")\nresource_hook_js_prepare_jscode = read_js_resource(\"_hook_js_prepare.js\")\nresource_hook_js_enhance_jscode = read_js_resource(\"_hook_js_enhance.js\")\nresource_hook_js_warp_jscode = read_js_resource(\"_hook_js_warp.js\")\n\ndef _init_resource_jscode():\n    global resource_rpc_jscode\n    global resource_hook_js_prepare_jscode\n    global resource_hook_js_enhance_jscode\n    resource_rpc_jscode = read_js_resource(\"rpc.js\")\n    resource_hook_js_prepare_jscode = read_js_resource(\"_hook_js_prepare.js\")\n    resource_hook_js_enhance_jscode = read_js_resource(\"_hook_js_enhance.js\")\n\n_init_resource_jscode()\n\ndef convert_jar_to_dex(jarfile: str) -> bool:\n    \"\"\"\n    将 JAR 文件转换为 DEX 文件\n\n    Args:\n        jarfile: JAR 文件路径\n\n    Returns:\n        转换成功返回 dex 文件路径，失败返回 None\n    \"\"\"\n    # 获取 dx/d8 文件路径\n    d8_file, dx_file = get_oldest_dx_d8_path()\n    if dx_file is None and d8_file is None:\n        warn(\n            \"error: Not found ANDROID_HOME. Please install android sdk and define ANDROID_HOME environment variable in your system\")\n        return None\n    # 检查输入文件\n    if not os.path.isfile(f\"{current_identifier}/{jarfile}\"):\n        warn(f\"error: JAR file not found: {jarfile}\")\n        return None\n    # 生成输出文件名（将 .jar 替换为 .dex）\n    if jarfile.endswith('.jar'):\n        dexfile = jarfile[:-4] + '.dex'\n    else:\n        dexfile = jarfile + '.dex'\n    out_put_dex_file = f'{current_identifier}/{dexfile}'\n    try:\n        if dx_file:\n            # 使用 dx 命令\n            cmd = [dx_file, '--dex', f'--output={out_put_dex_file}', f'{current_identifier}/{jarfile}']\n        else:\n            # 使用 d8 命令\n            cmd = [d8_file, '--output', current_identifier, f'{current_identifier}/{jarfile}']\n        info(f\"Converting {jarfile} to {dexfile}...\")\n        # 执行转换\n        result = subprocess.run(\n            cmd,\n            capture_output=True,\n            text=True,\n            check=True\n        )\n        # 检查输出文件\n        if os.path.exists(out_put_dex_file):\n            file_size = os.path.getsize(out_put_dex_file)\n            info(f\"Successfully converted to {dexfile} ({file_size} bytes)\")\n            return dexfile\n        else:\n            info(f\"error: Conversion failed, output file not created\")\n            if result.stderr:\n                info(f\"Error output: {result.stderr}\")\n            return None\n    except subprocess.CalledProcessError as e:\n        warn(f\"error: Conversion failed with exit code {e.returncode}\")\n        if e.stderr:\n            warn(f\"Error output: {e.stderr}\")\n        return None\n    except Exception as e:\n        warn(f\"error: {str(e)}\")\n        return None\n\ndef _init_frida_device():\n    global frida_device\n    def getRemoteDriver():\n        text = io.open(\".hooker_driver\",'r',encoding= 'utf8').read()\n        if not text:\n            return None\n        searchResult = re.search(r'\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+', text)\n        if searchResult:\n            return searchResult.group()\n        return None\n    if frida_device:\n        return\n    remoteDriver = getRemoteDriver() #ip:port\n    if remoteDriver:\n        frida_device = frida.get_device_manager().add_remote_device(remoteDriver)\n    else:\n        frida_device = frida.get_usb_device(1000)\n\n_init_frida_device()\n\n@dataclass\nclass AppInfo:\n    name: str               # 应用名（label，拿不到时用包名代替）\n    identifier: str         # 包名\n    pid: Optional[int]      # 运行中才有 pid，否则 None\n\ndef _list_third_party_packages() -> List[str]:\n    \"\"\"\n    第三方包（-3）最稳定。\n    输出行形如：package:com.xxx.yyy\n    \"\"\"\n    out = run_su_command(\"pm list packages -3\")\n    pkgs = []\n    for line in out.splitlines():\n        line = line.strip()\n        if line.startswith(\"package:\"):\n            pkgs.append(line.split(\"package:\", 1)[1])\n    return pkgs\n\n\ndef _get_pid_map() -> Dict[str, int]:\n    \"\"\"\n    获取运行中进程的 pid 映射：package -> pid\n    优先 pidof（快），不行再回退 ps。\n    \"\"\"\n    pid_map: Dict[str, int] = {}\n    # 方案 A：pidof（Android 8+ 大多支持）\n    # pidof 可能返回多个 pid（多进程），这里取第一个\n    # 但我们需要逐包调用 pidof，太慢，所以改用 ps 做一次性扫描更划算。\n    # 因此优先方案 B：ps -A 解析。\n    ps_out = run_su_command(\"ps -A -o PID,NAME\")\n    # 输出示例：\n    # PID NAME\n    #  123 com.xxx.yyy\n    for line in ps_out.splitlines()[1:]:\n        line = line.strip()\n        if not line:\n            continue\n        parts = re.split(r\"\\s+\", line, maxsplit=1)\n        if len(parts) != 2:\n            continue\n        pid_s, name = parts\n        if name.startswith(\"com.\"):  # 简单过滤：只收集 app 进程\n            try:\n                pid_map[name] = int(pid_s)\n            except ValueError:\n                pass\n    return pid_map\n\n\ndef _get_app_label_fast(pkg: str) -> Optional[str]:\n    \"\"\"\n    尝试快速拿 label（不保证所有 ROM 都能拿到）。\n    方法：cmd package dump + grep\n    失败就返回 None（用包名代替）。\n    \"\"\"\n    # 有些系统支持：cmd package resolve-activity / dumpsys package\n    # 但 label 不一定直接给；dumpsys package <pkg> 里通常能找到 \"application-label:\"\n    out = run_su_command(f\"dumpsys package {pkg} | grep -m 1 -E 'application-label:'\")\n    m = re.search(r\"application-label:'(.*)'\", out)\n    if m:\n        return m.group(1).strip()\n    return pkg\n\n\ndef enumerate_applications_adbutils(third_party_only: bool = True, include_label: bool = False) -> List[AppInfo]:\n    \"\"\"\n    用 adbutils 实现一个“类 enumerate_applications”的方法：\n    - identifier: 包名\n    - pid: 运行中的进程 pid（取主进程名 == 包名的情况）\n    - name: 可选，从 dumpsys 拿 label；默认用包名代替（快很多）\n    \"\"\"\n    apps: List[AppInfo] = []\n    # pkgs = _list_third_party_packages()\n    # info(f\"pkgs: {pkgs}\")\n    # pid_map = _get_pid_map()\n    # info(f\"is_magisk_root3:{is_magisk_root}\")\n    # for pkg in pkgs:\n    #     pid = pid_map.get(pkg, 0)  # 只有“进程名==包名”的主进程才会命中\n    #     if include_label:\n    #         info(f\"_get_app_label_fast:{pkg}\")\n    #         label = _get_app_label_fast(pkg) or pkg\n    #     else:\n    #         label = pkg\n    #     apps.append(AppInfo(name=label, identifier=pkg, pid=pid))\n    # info(f\"enumerate_applications_adbutils: {apps}\")\n    return frida_device.enumerate_applications()\n\ndef start_app(package_name):\n    global current_identifier_pid\n    shell_result = adb_device.shell(f\"dumpsys package {package_name} | grep -A 1 MAIN | grep {package_name}\").strip()\n    m = re.search(r\"\\s+([^\\s]+)\\s+filter\", shell_result)\n    if m:\n        main_activity = m.group(1)\n        adb_device.shell(f\"am start -n {main_activity}\")\n    else:\n        adb_device.shell(f\"monkey -p {package_name} -c android.intent.category.LAUNCHER 1\")\n    for j in range(20):\n        time.sleep(0.5)\n        out = adb_device.shell(f\"pidof {package_name}\").strip()\n        if out and out.isdigit():\n            current_identifier_pid = int(out)\n            return current_identifier_pid, current_identifier_name\n    return None, None\n\ndef restart_app(package_name):\n    global current_identifier_pid\n    info(f\"Restarting {current_identifier_name} Please wait for a few seconds\")\n    adb_device.app_stop(package_name)\n    time.sleep(1)\n    app_pid, app_name = start_app(package_name)\n    current_identifier_pid = app_pid\n\ndef ensure_app_in_foreground(package_name):\n    def get_main_pid(pkg):\n        out = adb_device.shell(f\"pidof {pkg}\").strip()\n        if out:\n            return int(out.split()[0])\n        return None\n    uid = None\n    shell_result = adb_device.shell(f\"dumpsys package {package_name}\").strip()\n    matchx = re.search(r\"(userId|uid|appId)=(\\d+)\", shell_result)\n    if matchx:\n        uid = int(matchx.group(2))\n    else:\n        warn(\"UID not found.\")\n    apk_path = adb_device.shell(f\"pm path {package_name}\").strip().replace(\"package:\", \"\")\n    if \"priv-app\" in apk_path:\n        apk_path = apk_path[:apk_path.index(\".apk\")+8]\n    else:\n        apk_path = apk_path[:apk_path.index(\"base.apk\")+8]\n    #print(f\"apk_path:{apk_path}\")\n    appinstall_path = apk_path.rsplit(\"/\", 1)[0]\n    appinstall_path_apkfilename = apk_path.rsplit(\"/\", 1)[1]\n    appinfo = None\n    version_name = None\n    if 'app_info' in dir(adb_device):\n        appinfo = adb_device.app_info(package_name)\n        version_name = appinfo.version_name\n    else:\n        appinfo = adb_device.package_info(package_name)\n        version_name = appinfo[\"version_name\"]\n    # 获取当前正在运行的所有进程\n    proc_map = {}\n    apps = enumerate_applications_adbutils(third_party_only=True, include_label=True)\n    for app in sorted(apps, key=lambda x: x.pid or 0):\n        if app.pid != 0:  # 只列出运行中的\n            proc_map[app.identifier] = (app.pid, app.name)\n    is_running = package_name in proc_map\n    # 获取当前前台 activity\n    foreground_output = adb_device.shell(\"dumpsys activity activities | grep mResumedActivity\")\n    is_foreground = package_name in foreground_output\n    if is_running:\n        if is_foreground:\n            info(f\"✅ App {package_name} is already in the foreground\")\n        else:\n            info(f\"📲 App {package_name} is running in the background, bringing it to the foreground...\")\n            # 通过 am 启动主 Activity，会自动 bring 到前台\n            adb_device.shell(f\"monkey -p {package_name} -c android.intent.category.LAUNCHER 1\")\n            #print(\"proc_map[package_name][1]\", proc_map[package_name][1])\n        main_pid = get_main_pid(package_name)\n        app_pid = main_pid if main_pid is not None else proc_map[package_name][0]\n        return app_pid, proc_map[package_name][1], version_name, appinstall_path, appinstall_path_apkfilename, uid\n    else:\n        info(f\"🚀 App {package_name} is not running, starting it now...\")\n        #adb_device.shell(f\"monkey -p {package_name} -c android.intent.category.LAUNCHER 1\")\n        app_pid, app_name = start_app(package_name)\n        main_pid = get_main_pid(package_name)\n        app_pid = main_pid if main_pid is not None else app_pid\n        return app_pid, app_name, version_name, appinstall_path, appinstall_path_apkfilename, uid\n\ndef get_remote_file_md5(file_path):\n    # 检查文件是否存在并获取长度\n    check_cmd = f\"md5sum {file_path}\"\n    result = run_su_command(check_cmd).strip()\n    if \"No such file\" in result or \"Permission denied\" in result or not result:\n        #warn(\"No such file\")\n        return \"\"\n    try:\n        # 56cf2745f4884b4dfcc1e193d0118c05  radar.dex\n        m = re.search(r\"[\\w]{32}\", result)\n        if m:\n            return m.group()\n        else:\n            return \"\"\n    except Exception:\n        return \"\"\n    \ndef get_local_file_md5(filepath, chunk_size=8192):\n    md5 = hashlib.md5()\n    try:\n        with open(filepath, 'rb') as f:\n            while True:\n                chunk = f.read(chunk_size)\n                if not chunk:\n                    break\n                md5.update(chunk)\n        return md5.hexdigest()\n    except FileNotFoundError:\n        warn(f\"File Not Found: {filepath}\")\n        return None\n    \ndef read_local_file(filename):\n    return io.open(filename,'r',encoding= 'utf8').read()\n    \ndef check_dependency_files():\n    def process_dex_dependency_files():\n        compara_and_update_file(\"mobile-deploy/radar.dex\", \"/data/local/tmp/radar.dex\")\n        compara_and_update_file(\"mobile-deploy/libext64.so\", f\"/data/data/{current_identifier}/files/libext64.so\")\n        compara_and_update_file(\"mobile-deploy/libext.so\", f\"/data/data/{current_identifier}/files/libext.so\")\n    t = threading.Thread(target=process_dex_dependency_files)\n    t.daemon = True\n    t.start()\n             \ndef compara_and_update_file(local_file, remote_file):\n    local_md5 = get_local_file_md5(local_file)\n    local_filename = local_file.split(\"/\")[-1]\n    filename = remote_file.split(\"/\")[-1]\n    #print(\"filename:\", filename)\n    sdcard_remote_md5 = get_remote_file_md5(f\"/sdcard/{local_filename}\")\n    #先把radar.dex拷贝到sdcard，后期更新radar.dex直接从sdcard拷过去\n    if local_md5 != sdcard_remote_md5:\n        push_file_to_remote(local_file, \"/sdcard/\", False)\n    remote_md5 = get_remote_file_md5(remote_file)\n    #print(f\"local_md5:{local_md5} remote_md5:{remote_md5}\")\n    if local_md5 != remote_md5:\n        #info(f\"update file {local_filename} to {remote_file}\")\n        run_su_command(f\"cp /sdcard/{local_filename} {remote_file}\", True)\n        run_su_command(f\"chmod 555 {remote_file}\", True)\n\n\ndef on_message(message, data):\n    if message['type'] == 'send':\n        print(\"[*] {0}\".format(message['payload']))\n    elif message['type'] == 'error':\n        warn(\"[!] {0}\".format(message['stack']))\n    else:\n        print(message)\n        \ndef attach_rpc(use_v8=False):\n    global frida_device\n    online_session = None\n    online_script = None\n    online_session = frida_device.attach(current_identifier_pid)\n    if online_session == None:\n        warn(\"attaching fail to device\")\n        return None, None\n    if use_v8:\n        online_script = online_session.create_script(resource_rpc_jscode, runtime=\"v8\")\n    else:\n        online_script = online_session.create_script(resource_rpc_jscode)\n    online_script.on('message', on_message)\n    online_script.load()\n    # online_script.exports_sync.loadradardex()\n    return online_session, online_script\n\ndef attach(script_file, use_v8=False):\n    if not os.path.isfile(script_file):\n        warn(f\"attach {script_file} File Not found\")\n        return None, None\n    script_jscode = read_local_file(script_file)\n    script_jscode = script_jscode + \"\\n\\n\\n\" + resource_hook_js_warp_jscode\n    global frida_device\n    online_session = None\n    online_script = None\n    online_session = frida_device.attach(current_identifier_pid)\n    if online_session == None:\n        warn(\"attaching fail to device\")\n        return None, None\n    if use_v8:\n        #info(\"use v8 js engine\")\n        online_script = online_session.create_script(script_jscode, runtime=\"v8\")\n    else:\n        online_script = online_session.create_script(script_jscode)\n    online_script.on('message', on_message)\n    online_script.load()\n    #sys.stdin.read()\n    return online_session, online_script\n\ndef spawn(script_file, use_v8=False):\n    if not os.path.isfile(script_file):\n        warn(f\"{script_file} File Not found\")\n        return None, None\n    script_jscode = read_local_file(script_file)\n    script_jscode = script_jscode + \"\\n\\n\\n\" + resource_hook_js_warp_jscode\n    global frida_device\n    current_identifier_pid = frida_device.spawn([current_identifier])\n    online_script = None\n    online_session = frida_device.attach(current_identifier_pid)\n    if online_session == None:\n        warn(\"attaching fail to device\")\n        return None, None\n    if use_v8:\n        online_script = online_session.create_script(script_jscode, runtime=\"v8\")\n    else:\n        online_script = online_session.create_script(script_jscode)\n    online_script.on('message', on_message)\n    online_script.load()\n    release_version = int(adb_device.prop.get(\"ro.build.version.release\").split('.', 1)[0])\n    if release_version >= 12:\n        frida_device.resume(current_identifier_pid)\n    else:\n        frida_device.resume(current_identifier)\n    #sys.stdin.read()\n    return online_session, online_script\n    \n\ndef detach(online_session, online_script):\n    if online_script != None:\n        try:\n            online_script.exports_sync.cleanup()\n            online_script.unload()\n        except Exception as e:\n            info(f\"RPC cleanup failed: {e}\")\n    if online_session != None:\n        try:\n            online_session.detach()\n        except Exception as e:\n            info(f\"detach failed: {e}\")\n        \n \ndef exists_class(target, className):\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script,_ = attach_rpc(target);\n        info(online_script.exports_sync.containsclass(className))\n    except Exception:\n        warn(traceback.format_exc())  \n    finally:    \n        detach(online_session, online_script)\n\ndef create_workingdir_file(filename, text):\n    file = None\n    try:\n        file = open(filename, mode='w+')\n        file.write(text)\n    except Exception:\n        warn(traceback.format_exc())  \n    finally:\n        if file != None:\n            file.close()\n            \ndef create_working_dir_enverment():\n    global current_identifier\n    global frida_device\n    global current_identifier_name\n    global current_identifier_version\n    packageName = current_identifier\n    if not os.path.exists(packageName):\n        os.makedirs(packageName)\n        info(f\"Creating working directory: {packageName}\")\n        info(f\"Generating frida shortcut command...\")\n        os.makedirs(packageName+\"/xinit\")\n        shellPrefix = \"#!/bin/bash\\nHOOKER_DRIVER=$(cat ../.hooker_driver)\\n\"\n        logHooking = shellPrefix + \"echo \\\"hooking $1\\\" > log\\ndate | tee -ai log\\n\" + \"frida $HOOKER_DRIVER -l $1 -N \" + packageName + \" | tee -ai log\"\n        attach_shell = shellPrefix + \"frida $HOOKER_DRIVER -l $1 -N \" + packageName\n        spawn_shell = f\"{shellPrefix}\\nfrida $HOOKER_DRIVER --runtime=v8 -f {packageName} -l $1\"\n        create_workingdir_file(packageName+\"/hooking\", logHooking)\n        create_workingdir_file(packageName+\"/attach\", attach_shell)\n        create_workingdir_file(packageName+\"/spawn\", spawn_shell)\n        create_workingdir_file(packageName + \"/kill\", shellPrefix + \"frida-kill $HOOKER_DRIVER \"+packageName)\n        create_workingdir_file(packageName+\"/objection\", shellPrefix + \"objection -d -g \"+packageName+\" explore\")\n        os.popen('chmod 777 ' + packageName +'/hooking').readlines()\n        os.popen('chmod 777 ' + packageName +'/attach').readlines()\n        os.popen('chmod 777 ' + packageName +'/objection').readlines()\n        os.popen('chmod 777 ' + packageName +'/spawn').readlines()\n        info(f\"Generating built-in frida script...\")\n        init_js_files = [\n            \"url.js\",\n            \"android_ui.js\",\n            \"edit_text.js\",\n            \"text_view.js\",\n            \"click.js\",\n            \"activity_events.js\",\n            \"object_store.js\",\n            \"keystore_dump.js\",\n            \"ssl_log.js\",\n            \"just_trust_me.js\",\n            \"just_trust_me_for_ios.js\",\n            \"hook_register_natives.js\",\n            \"dump_dex.js\",\n            \"trace_init_proc.js\",\n            \"hook_artmethod_register.js\",\n            \"find_anit_frida_so.js\",\n            \"hook_jni_method_trace.js\",\n            \"replace_dlsym_get_pthread_create.js\",\n            \"find_boringssl_custom_verify_func.js\",\n            \"get_device_info.js\",\n            \"apk_shell_scanner.js\",\n            \"bypass_frida_svc_detect.js\",\n            \"bypass_root_detect.js\",\n            \"bypass_vpn_detect.js\",\n            \"hook_encryption_algo.js\",\n            \"hook_encryption_algo2.js\",\n            \"webview_enable_debug.js\",\n            \"hook_proxy_check.js\"\n        ]\n        for js_file in init_js_files:\n            if not os.path.exists(f\"js/{js_file}\"):\n                info(f\"File not Found: js/{js_file}\")\n                continue\n            jscode = read_js_resource(js_file)\n            create_workingdir_file(f\"{packageName}/{js_file}\", jscode.replace(\"com.smile.gifmaker\", packageName))\n        info(f\"Copying APK {current_identifier_install_path}/{current_identifier_install_apkfilename} to working directory please waiting for a few seconds\")\n        global current_local_apk_path\n        current_local_apk_path = f\"{packageName}/{current_identifier_name.replace(' ', '')}_{current_identifier_version}.apk\"\n        pull_file_to_local(f\"{current_identifier_install_path}/{current_identifier_install_apkfilename}\", current_local_apk_path)\n        info(f\"Working directory create successful\")\n        \ndef init_working_dir_enverment():\n    global current_local_apk_path\n    current_local_apk_path = f\"{current_identifier}/{current_identifier_name.replace(' ', '')}_{current_identifier_version}.apk\"\n    if os.path.isfile(current_local_apk_path):\n        return\n    if os.path.isdir(current_local_apk_path):\n        os.popen(f'rm -rf {current_local_apk_path}').readlines()\n    #print(f\"current_identifier_install_path:{current_identifier_install_path}\")\n    pull_file_to_local(f\"{current_identifier_install_path}/{current_identifier_install_apkfilename}\", current_local_apk_path)\n    info(f\"Working directory init successful\")\n        \ndef hook_js(hookCmdArg, savePath = None):\n    online_session = None\n    online_script = None\n    packageName = current_identifier\n    try:\n        ganaretoionJscode = \"\"\n        online_session, online_script = attach_rpc(use_v8=False);\n        appversion = current_identifier_version\n        spaceSpatrater = hookCmdArg.find(\":\")\n        className = hookCmdArg\n        toSpace = \"*\"\n        file_method_name = \"allfunc\"\n        if spaceSpatrater > 0:\n            className = hookCmdArg[:spaceSpatrater]\n            toSpace = hookCmdArg[spaceSpatrater+1:]\n            if \"<init>\" in toSpace:\n                toSpace = \"_\"\n                file_method_name = \"_init\"\n            else:\n                toSpace = toSpace.split(\"(\")[0]\n                file_method_name = toSpace\n        if not online_script.exports_sync.containsclass(className):\n            warn(f\"Class Not Found {className}\")\n            return\n        jscode = online_script.exports_sync.hookjs(className, toSpace);\n        ganaretoionJscode += (\"\\n//\"+hookCmdArg+\"\\n\")\n        ganaretoionJscode += jscode\n        if savePath == None:\n            defaultFilename = className.replace(\":\", \".\").replace(\"$\", \".\").replace(\"__\", \".\")+ \".\" + file_method_name + \".js\"\n            savePath = packageName+\"/\"+defaultFilename;\n        else:\n            defaultFilename = savePath\n            savePath = packageName+\"/\"+savePath;\n        if len(ganaretoionJscode):\n            ganaretoionJscode = resource_hook_js_prepare_jscode + \"\\n\" + ganaretoionJscode + \"\\n\\n\\n\\n\\n//---------------------may be you need--------------------\\n\\n\" + resource_hook_js_enhance_jscode\n            warpExtraInfo = f\"//cracked by {current_identifier_name} {appversion}\\n\"\n            warpExtraInfo += \"//\"+hookCmdArg + \"\\n\\n\"\n            warpExtraInfo += ganaretoionJscode\n            create_workingdir_file(savePath, jsbeautifier.beautify(warpExtraInfo))\n            info(\"frida hook script: \" + defaultFilename)\n        else:\n            warn(\"Not found any classes by pattern \"+hookCmdArg+\".\")\n    except Exception:\n        warn(traceback.format_exc())  \n    finally:    \n        detach(online_session, online_script)\n        \ndef print_activitys():\n    online_session = None\n    online_script = None\n    try:\n        online_session,online_script = attach_rpc();\n        info(online_script.exports_sync.activitys())\n    except Exception:\n        print(traceback.format_exc())  \n    finally:\n        detach(online_session, online_script)\n        \ndef print_services():\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script = attach_rpc();\n        info(online_script.exports_sync.services())\n    except Exception:\n        print(traceback.format_exc())  \n    finally:\n        detach(online_session, online_script)\n\ndef print_object(objectId):\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script = attach_rpc();\n        info(online_script.exports_sync.objectinfo(objectId))\n    except Exception:\n        print(traceback.format_exc())  \n    finally:\n        detach(online_session, online_script)\n        \ndef object_to_explain(objectId):\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script = attach_rpc();\n        info(online_script.exports_sync.objecttoexplain(objectId))\n    except Exception:\n        print(traceback.format_exc())  \n    finally:\n        detach(online_session, online_script)\n\ndef print_view(viewId):\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script = attach_rpc();\n        report = online_script.exports_sync.viewinfo(viewId)\n        info(report);\n    except Exception:\n        print(traceback.format_exc())  \n    finally:\n        detach(online_session, online_script)\n\ndef rpc_start_web_server(dex_file, all_class):\n    global webserver_url\n    online_session = None\n    online_script = None\n    try:\n        online_session, online_script = attach_rpc();\n        text = online_script.exports_sync.starthttpserver(dex_file, \",\".join(all_class))\n        info(text)\n        m = re.search(\"http:[^s]+:[\\d]+\", text)\n        if m:\n            webserver_url = m.group(0)\n        else:\n            info(\"找不到webserver\" + text)\n    except Exception:\n        print(traceback.format_exc())\n    finally:\n        detach(online_session, online_script)\n        \n\ndef list_working_dir():\n    js_files = {\n        filename: None\n        for filename in os.listdir(current_identifier)\n        if filename.endswith(\".js\")\n    }\n    print_js_file(list(js_files.keys()))\n                \n                \ndef execute_script(script_file, is_spawn=False):\n    if not os.path.isfile(f\"{current_identifier}/{script_file}\"):\n        warn(f\"{current_identifier}/{script_file} File Not found\")\n        return\n    online_session = None\n    online_script = None\n    log_fh = None\n    saved_fd = None\n    pipe_r = None\n    pipe_w = None\n    tee_thread = None\n    use_v8 = \"just_trust_me.js\" in script_file\n    try:\n        log_filename = script_file.rsplit('.', 1)[0] + '.log'\n        log_filepath = f\"{current_identifier}/{log_filename}\"\n        log_fh = open(log_filepath, 'w', encoding='utf-8')\n        # fd 级别重定向，捕获所有 C 层写 stdout 的输出\n        pipe_r, pipe_w = os.pipe()\n        saved_fd = os.dup(1)\n        os.dup2(pipe_w, 1)\n        os.close(pipe_w)\n        pipe_w = None\n        stop_event = threading.Event()\n        def tee_reader():\n            while not stop_event.is_set():\n                try:\n                    data = os.read(pipe_r, 8192)\n                    if not data:\n                        break\n                    os.write(saved_fd, data)\n                    log_fh.write(data.decode('utf-8', errors='replace'))\n                    log_fh.flush()\n                except Exception:\n                    break\n        tee_thread = threading.Thread(target=tee_reader, daemon=True)\n        tee_thread.start()\n        if is_spawn:\n            online_session, online_script = spawn(f\"{current_identifier}/{script_file}\", use_v8)\n        else:\n            online_session, online_script = attach(f\"{current_identifier}/{script_file}\", use_v8)\n        info(f\"Frida output logging -> {log_filepath}\")\n        while online_session != None:\n            try:\n                with patch_stdout():\n                    text = cmd_session.prompt(\"CTRL + C to stop > \", handle_sigint=True)\n            except KeyboardInterrupt:\n                info(f\"Interrupting {script_file}\")\n                break\n            except EOFError:\n                warn(\"Exiting...\")\n                break\n    except Exception:\n        print(traceback.format_exc())\n    finally:\n        detach(online_session, online_script)\n        # 恢复 fd 1\n        if saved_fd is not None:\n            os.dup2(saved_fd, 1)\n            os.close(saved_fd)\n        if pipe_r is not None:\n            os.close(pipe_r)\n        if tee_thread is not None:\n            stop_event.set()\n        if log_fh is not None:\n            log_fh.close()\n        info(f\"{script_file} detach successful\")\n        if is_spawn:\n            restart_app(current_identifier)\n            \ndef just_trust_me():\n    execute_script(\"just_trust_me.js\", True)\n    \n\ndef r0capture():\n    PY3K = sys.version_info >= (3, 0)\n    # --- workaround against Python consistency issues\n    def normalize_py():\n        if sys.platform == \"win32\":\n            # set sys.stdout to binary mode on Windows\n            import os, msvcrt\n            msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\n    \n    # --- - chunking helpers\n    def chunks(seq, size):\n        d, m = divmod(len(seq), size)\n        for i in range(d):\n            yield seq[i * size:(i + 1) * size]\n        if m:\n            yield seq[d * size:]\n    \n    \n    def chunkread(f, size):\n        c = f.read(size)\n        while len(c):\n            yield c\n            c = f.read(size)\n    \n    \n    def genchunks(mixed, size):\n        if hasattr(mixed, 'read'):\n            return chunkread(mixed, size)\n        else:\n            return chunks(mixed, size)\n    \n    \n    # --- - /chunking helpers\n    def dehex(hextext):\n        if PY3K:\n            return bytes.fromhex(hextext)\n        else:\n            hextext = \"\".join(hextext.split())\n            return hextext.decode('hex')\n        \n    def dump(binary, size=2, sep=' '):\n        hexstr = binascii.hexlify(binary)\n        if PY3K:\n            hexstr = hexstr.decode('ascii')\n        return sep.join(chunks(hexstr.upper(), size))\n    \n    \n    def dumpgen(data, only_str):\n        generator = genchunks(data, 16)\n        for addr, d in enumerate(generator):\n            line = \"\"\n            if not only_str:\n                # 00000000:\n                line = '%08X: ' % (addr * 16)\n                # 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00\n                dumpstr = dump(d)\n                line += dumpstr[:8 * 3]\n                if len(d) > 8:  # insert separator if needed\n                    line += ' ' + dumpstr[8 * 3:]\n                # ................\n                # calculate indentation, which may be different for the last line\n                pad = 2\n                if len(d) < 16:\n                    pad += 3 * (16 - len(d))\n                if len(d) <= 8:\n                    pad += 1\n                line += ' ' * pad\n    \n            for byte in d:\n                # printable ASCII range 0x20 to 0x7E\n                if not PY3K:\n                    byte = ord(byte)\n                if 0x20 <= byte <= 0x7E:\n                    line += chr(byte)\n                else:\n                    line += '.'\n            yield line\n    \n    \n    def hexdump(data, result='print', only_str=False):\n        if PY3K and type(data) == str:\n            raise TypeError('Abstract unicode data (expected bytes sequence)')\n        gen = dumpgen(data, only_str=only_str)\n        if result == 'generator':\n            return gen\n        elif result == 'return':\n            return '\\n'.join(gen)\n        elif result == 'print':\n            for line in gen:\n                print(line)\n        else:\n            raise ValueError('Unknown value of `result` argument')\n    banner = (\n        \"--------------------------------------------------------------------------------------------\\n\"\n        \"           .oooo.                                      .                                  \\n\"\n        \"          d8P'`Y8b                                   .o8                                  \\n\"\n        \"oooo d8b 888    888  .ooooo.   .oooo.   oo.ooooo.  .o888oo oooo  oooo  oooo d8b  .ooooo.  \\n\"\n        \"`888\\\"\\\"8P 888    888 d88' `\\\"Y8 `P  )88b   888' `88b   888   `888  `888  `888\\\"\\\"8P d88' `88b \\n\"\n        \" 888     888    888 888        .oP\\\"888   888   888   888    888   888   888     888ooo888 \\n\"\n        \" 888     `88b  d88' 888   .o8 d8(  888   888   888   888 .  888   888   888     888    .o \\n\"\n        \"d888b     `Y8bd8P'  `Y8bod8P' `Y888\\\"\\\"8o  888bod8P'   \\\"888\\\"  `V88V\\\"V8P' d888b    `Y8bod8P' \\n\"\n        \"                                         888                                               \\n\"\n        \"                                        o888o                                              \\n\"\n        \"                    https://github.com/r0ysue/r0capture\\n\"\n        \"--------------------------------------------------------------------------------------------\\n\"\n    )\n    print(banner)\n    ssl_sessions = {}\n    def ssl_log(pcap=None, verbose=False, ssllib=\"\", wait=0):\n        # if platform.system() not in (\"Darwin\", \"Linux\"):\n        #   raise NotImplementedError(\"This function is only implemented for Linux and \"\n        #                             \"macOS systems.\")\n    \n        def log_pcap(pcap_file, ssl_session_id, function, src_addr, src_port,\n                     dst_addr, dst_port, data):\n            t = time.time()\n            if ssl_session_id not in ssl_sessions:\n                ssl_sessions[ssl_session_id] = (random.randint(0, 0xFFFFFFFF),\n                                                random.randint(0, 0xFFFFFFFF))\n            client_sent, server_sent = ssl_sessions[ssl_session_id]\n            if function == \"SSL_read\":\n                seq, ack = (server_sent, client_sent)\n            else:\n                seq, ack = (client_sent, server_sent)\n            for writes in (\n                    # PCAP record (packet) header\n                    (\"=I\", int(t)),  # Timestamp seconds\n                    (\"=I\", int((t * 1000000) % 1000000)),  # Timestamp microseconds\n                    (\"=I\", 40 + len(data)),  # Number of octets saved\n                    (\"=i\", 40 + len(data)),  # Actual length of packet\n                    # IPv4 header\n                    (\">B\", 0x45),  # Version and Header Length\n                    (\">B\", 0),  # Type of Service\n                    (\">H\", 40 + len(data)),  # Total Length\n                    (\">H\", 0),  # Identification\n                    (\">H\", 0x4000),  # Flags and Fragment Offset\n                    (\">B\", 0xFF),  # Time to Live\n                    (\">B\", 6),  # Protocol\n                    (\">H\", 0),  # Header Checksum\n                    (\">I\", src_addr),  # Source Address\n                    (\">I\", dst_addr),  # Destination Address\n                    # TCP header\n                    (\">H\", src_port),  # Source Port\n                    (\">H\", dst_port),  # Destination Port\n                    (\">I\", seq),  # Sequence Number\n                    (\">I\", ack),  # Acknowledgment Number\n                    (\">H\", 0x5018),  # Header Length and Flags\n                    (\">H\", 0xFFFF),  # Window Size\n                    (\">H\", 0),  # Checksum\n                    (\">H\", 0)):  # Urgent Pointer\n                pcap_file.write(struct.pack(writes[0], writes[1]))\n            pcap_file.write(data)\n            if function == \"SSL_read\":\n                server_sent += len(data)\n            else:\n                client_sent += len(data)\n            ssl_sessions[ssl_session_id] = (client_sent, server_sent)\n    \n        def r0capture_on_message(message, data):\n            if message[\"type\"] == \"error\":\n                logger.info(f\"{message}\")\n                os.kill(os.getpid(), signal.SIGTERM)\n                return\n            if len(data) == 1:\n                logger.info(f'{message[\"payload\"][\"function\"]}')\n                logger.info(f'{message[\"payload\"][\"stack\"]}')\n                return\n            p = message[\"payload\"]\n            if verbose:\n                src_addr = socket.inet_ntop(socket.AF_INET,\n                                            struct.pack(\">I\", p[\"src_addr\"]))\n                dst_addr = socket.inet_ntop(socket.AF_INET,\n                                            struct.pack(\">I\", p[\"dst_addr\"]))\n                session_id = p['ssl_session_id']\n                logger.info(f\"SSL Session: {session_id}\")\n                logger.info(\"[%s] %s:%d --> %s:%d\" % (\n                    p[\"function\"],\n                    src_addr,\n                    p[\"src_port\"],\n                    dst_addr,\n                    p[\"dst_port\"]))\n                gen = hexdump(data, result=\"generator\",only_str=True)\n                str_gen = ''.join(gen)\n                logger.info(f\"{str_gen}\")\n                logger.info(f\"{p['stack']}\")\n            if pcap:\n                log_pcap(pcap_file, p[\"ssl_session_id\"], p[\"function\"], p[\"src_addr\"],\n                         p[\"src_port\"], p[\"dst_addr\"], p[\"dst_port\"], data)\n        current_identifier_pid = frida_device.spawn([current_identifier])        \n        online_session = frida_device.attach(current_identifier_pid)\n        time.sleep(1)\n        if wait > 0:\n            print(f\"wait for {wait} seconds\")\n            time.sleep(wait)\n            \n        if pcap:\n            pcap_file = open(pcap, \"wb\", 0)\n            for writes in (\n                    (\"=I\", 0xa1b2c3d4),  # Magic number\n                    (\"=H\", 2),  # Major version number\n                    (\"=H\", 4),  # Minor version number\n                    (\"=i\", time.timezone),  # GMT to local correction\n                    (\"=I\", 0),  # Accuracy of timestamps\n                    (\"=I\", 65535),  # Max length of captured packets\n                    (\"=I\", 228)):  # Data link type (LINKTYPE_IPV4)\n                pcap_file.write(struct.pack(writes[0], writes[1]))\n        r0capture_script = read_js_resource(\"r0capture.js\")\n        online_script = online_session.create_script(r0capture_script, runtime=\"v8\")\n        online_script.on(\"message\", r0capture_on_message)\n        online_script.load()\n        release_version = int(adb_device.prop.get(\"ro.build.version.release\").split('.', 1)[0])\n        if release_version >= 12:\n            frida_device.resume(current_identifier_pid)\n        else:\n            frida_device.resume(current_identifier)\n        if ssllib != \"\":\n            online_script.exports.setssllib(ssllib)\n        try:\n            while online_session != None:\n                try:\n                    with patch_stdout():\n                        text = cmd_session.prompt(\"CTRL + C to stop > \", handle_sigint=True)\n                except KeyboardInterrupt:\n                    info(f\"Interrupting\")\n                    break\n                except EOFError:\n                    warn(\"Exiting...\")\n                    break\n        except Exception:\n            print(traceback.format_exc())  \n        finally:\n            detach(online_session, online_script)\n            if pcap:\n                pcap_file.flush()\n                pcap_file.close()\n                info(f\"flushing {current_identifier}/r0capture_ssl.pcap successful\")\n            info(\"r0capture.js detach successful\")\n            restart_app(current_identifier)\n    ssl_log(f\"{current_identifier}/r0capture_ssl.pcap\", True)\n    \ndef un_proxy():\n    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\")\n    run_su_command(\"iptables -t nat -F REDSOCKS\")\n    run_su_command(\"iptables -t nat -D OUTPUT -p tcp -j REDSOCKS\")\n    run_su_command(\"iptables -t nat -X REDSOCKS\")\n    run_su_command(\"killall redsocks\")\n    run_su_command(\"pid=$(ps -ef | grep '[r]edsocks' | awk '{print $2}'); [ -n \\\"$pid\\\" ] && kill -9 $pid\")\n        \ndef set_proxy(proxy):\n    pattern = r'(http|socks5)://(\\d{2,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)$'\n    m = re.search(pattern, proxy.strip())\n    if not m:\n        warn(f\"proxy scheme error: {proxy}\")\n        return\n    proxy_type = m.group(1)\n    if proxy_type == \"http\":\n        proxy_type = \"http-relay\"\n    proxy_ip = m.group(2)\n    proxy_port = m.group(3)\n    config = (\n        \"base {\\n\"\n        \"    log_debug = on;\\n\"\n        \"    log_info = on;\\n\"\n        \"    daemon = on;\\n\"\n        \"    redirector = iptables;\\n\"\n        \"}\\n\\n\"\n    )\n    if proxy_type == \"http-relay\":\n        config += (\n            \"redsocks {\\n\"\n            \"    local_ip = 127.0.0.1;\\n\"\n            \"    local_port = 12345;\\n\"\n            f\"    ip = {proxy_ip};\\n\"\n            f\"    port = {proxy_port};\\n\"\n            f\"    type = http-relay;\\n\"\n            \"}\"\n        )\n    else:\n        config += (\n            \"redsocks {\\n\"\n            \"    local_ip = 127.0.0.1;\\n\"\n            \"    local_port = 12345;\\n\"\n            f\"    ip = {proxy_ip};\\n\"\n            f\"    port = {proxy_port};\\n\"\n            f\"    type = {proxy_type};\\n\"\n            \"}\"\n        )\n    if not check_remote_file_exists(\"/sdcard/redsocks\"):\n        push_file_to_remote(f\"mobile-deploy/redsocks\", \"/sdcard/redsocks\", False)\n        run_su_command(f\"cp /sdcard/redsocks /data/local/tmp/redsocks\")\n        run_su_command(f\"chmod 700 /data/local/tmp/redsocks\")\n    if not check_remote_file_exists(\"/sdcard/libevent-2.1.so\"):\n        push_file_to_remote(f\"mobile-deploy/libevent-2.1.so\", \"/sdcard/libevent-2.1.so\", False)\n        run_su_command(f\"cp /sdcard/libevent-2.1.so /data/local/tmp/libevent-2.1.so\")\n    if not check_remote_file_exists(\"/sdcard/libevent_core-2.1.so\"):\n        push_file_to_remote(f\"mobile-deploy/libevent_core-2.1.so\", \"/sdcard/libevent_core-2.1.so\", False)\n        run_su_command(f\"cp /sdcard/libevent_core-2.1.so /data/local/tmp/libevent_core-2.1.so\")\n    if not check_remote_file_exists(\"/data/local/tmp/redsocks\"):\n        run_su_command(f\"cp /sdcard/redsocks /data/local/tmp/redsocks\")\n        run_su_command(f\"cp /sdcard/libevent-2.1.so /data/local/tmp/libevent-2.1.so\")\n        run_su_command(f\"cp /sdcard/libevent_core-2.1.so /data/local/tmp/libevent_core-2.1.so\")\n        run_su_command(f\"chmod 700 /data/local/tmp/redsocks\")\n    un_proxy()\n    adb_device.shell(f\"rm -f /data/local/tmp/redsocks.conf\")\n    adb_device.shell(f\"echo '{config}' > /data/local/tmp/redsocks.conf\")\n    time.sleep(1)\n    run_su_command(f\"LD_LIBRARY_PATH=/data/local/tmp/ /data/local/tmp/redsocks -c /data/local/tmp/redsocks.conf\")\n    if proxy_type == \"http-relay\":\n        run_su_command(f\"iptables -t nat -N REDSOCKS\")\n        run_su_command(f\"iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN\")\n        run_su_command(f\"iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN\")\n        run_su_command(f\"iptables -t nat -A REDSOCKS -d 192.168.1.0/24 -j RETURN\")\n        run_su_command(f\"iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345\")\n        # run_su_command(f\"iptables -t nat -L -nv\")\n        # run_su_command(f\"iptables -t nat -A OUTPUT -p tcp  --dport 80 -j REDIRECT --to 12345\")\n        # run_su_command(f\"iptables -t nat -A OUTPUT -p tcp  --dport 443 -j REDIRECT --to 12345\")\n    elif proxy_type.startswith(\"socks\"):\n        run_su_command(f\"iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner {current_identifier_uid} -j REDIRECT --to-ports 12345\")\n        #run_su_command(f\"iptables -t nat -A PREROUTING -i wlan0 -p tcp -j REDIRECT --to-ports 12345\")\n        info(f\"proxy {proxy} OK\")\n    else:\n        warn(f\"Cannot set proxy {proxy}\")\n        return\nif not os.path.exists('.cache'):\n    os.makedirs('.cache')\n    \ndef open_or_create_db():\n    global current_identifier_cache_db\n    if current_identifier_cache_db:\n        return current_identifier_cache_db\n    db_path=f'.cache/{current_identifier}_{current_identifier_version}_class_methods.db'\n    conn = sqlite3.connect(db_path, check_same_thread=False)\n    current_identifier_cache_db = conn\n    cursor = conn.cursor()\n    # 检查是否存在 app_methods 表\n    cursor.execute('''\n        SELECT name FROM sqlite_master \n        WHERE type='table' AND name='app_methods'\n    ''')\n    table_exists = cursor.fetchone()\n    if table_exists:\n        cursor.execute(\"PRAGMA table_info(app_methods)\")\n        columns = [row[1] for row in cursor.fetchall()]\n        if 'filename' not in columns:\n            cursor.execute(\"DROP TABLE IF EXISTS app_methods\")\n            conn.commit()\n            table_exists = False\n    if not table_exists:\n        # 创建表\n        cursor.execute('''\n            CREATE TABLE app_methods (\n                id INTEGER PRIMARY KEY AUTOINCREMENT,\n                filename TEXT NOT NULL,\n                class_package_name TEXT NOT NULL,\n                class_name TEXT NOT NULL,\n                method_name TEXT NOT NULL,\n                readable_proto_list TEXT\n            )\n        ''')\n        # 创建索引\n        cursor.execute('CREATE INDEX idx_class_package_prefix ON app_methods(class_package_name)')\n        cursor.execute('CREATE INDEX idx_class_name ON app_methods(class_name)')\n        conn.commit()\n    return current_identifier_cache_db\n\ndef insert_if_not_exists(cursor, filename, class_package_name, class_name,\n                         method_name, readable_proto_list):\n    try:\n        # 查询是否存在相同 class_package_name 和 class_name 的记录\n        cursor.execute('''\n            SELECT 1 FROM app_methods\n            WHERE class_package_name = ? AND class_name = ?\n            LIMIT 1\n        ''', (class_package_name, class_name))\n        exists = cursor.fetchone()\n        if not exists:\n            cursor.execute('''\n                INSERT INTO app_methods (\n                    filename, class_package_name, class_name,\n                    method_name, readable_proto_list\n                ) VALUES (?, ?, ?, ?, ?)\n            ''', (\n                filename, class_package_name, class_name,\n                method_name, readable_proto_list\n            ))\n            current_identifier_cache_db.commit()\n    except sqlite3.DatabaseError as e:\n        pass\n    return exists\n        \ndef ensure_readonly_copy_fresh():\n    \"\"\"\n    确保只读数据库副本是最新的，如果过旧或主库有更新则拷贝。\n    设置 current_identifier_cache_readonly_db 为连接对象。\n    \"\"\"\n    global current_identifier_cache_readonly_db  # 注意声明为全局变量\n    base_path = f'.cache/{current_identifier}_{current_identifier_version}_class_methods.db'\n    readonly_path = f'.cache/{current_identifier}_{current_identifier_version}_tmp_readonly_class_methods.db'\n    # 主库不存在直接报错\n    if not os.path.exists(base_path):\n        raise FileNotFoundError(f\"主数据库不存在: {base_path}\")\n    now = time.time()\n    # 如果只读文件不存在，则直接拷贝\n    if not os.path.exists(readonly_path):\n        shutil.copy2(base_path, readonly_path)\n    else:\n        readonly_ctime = os.path.getctime(readonly_path)\n        # 如果只读副本距现在小于30秒，则不更新\n        if now - readonly_ctime < 30:\n            pass  # 不拷贝\n        else:\n            base_ctime = os.path.getctime(base_path)\n            if base_ctime > readonly_ctime:\n                shutil.copy2(base_path, readonly_path)\n\n    # 连接（无论是否拷贝，都会使用只读副本连接）\n    readonly_uri = f'file:{readonly_path}?mode=ro'\n    current_identifier_cache_readonly_db = sqlite3.connect(readonly_uri, uri=True, check_same_thread=False)\n\n        \ndef count_methods_by_app_version():\n    ensure_readonly_copy_fresh()\n    cursor = current_identifier_cache_readonly_db.cursor()\n    cursor.execute('SELECT COUNT(*) FROM app_methods')\n    count = cursor.fetchone()[0]\n    return count\n\ndef query_class_name_by_prefix(class_name_prefix, class_name, limit=15):\n    # 拷贝最新主库文件（如果需要）\n    ensure_readonly_copy_fresh()\n    cursor = current_identifier_cache_readonly_db.cursor()\n    if class_name and not \".\" in class_name:\n        if class_name_prefix:\n            cursor.execute('''\n                SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n                WHERE class_name = ? AND class_package_name LIKE ?\n                LIMIT ?\n            ''', (class_name, f'%{class_name_prefix}%', limit))\n            #print(\"1\")\n            results = cursor.fetchall()\n            if results:\n                return results\n            cursor.execute('''\n                SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n                WHERE class_name LIKE ? AND class_package_name LIKE ?\n                LIMIT ?\n            ''', (f'{class_name}%', f'%{class_name_prefix}%', limit))\n            #print(\"2\")\n            results = cursor.fetchall()\n            if results:\n                return results\n        else:\n            cursor.execute('''\n                SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n                WHERE class_name = ?\n                LIMIT ?\n            ''', (class_name, limit))\n            #print(\"3\")\n            results = cursor.fetchall()\n            if results:\n                return results\n            cursor.execute('''\n                SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n                WHERE class_name LIKE ?\n                LIMIT ?\n            ''', (f'{class_name}%', limit))\n            #print(\"4\")\n            results = cursor.fetchall()\n            if results:\n                return results\n        return []\n    cursor.execute('''\n        SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n        WHERE class_package_name LIKE ?\n        LIMIT ?\n    ''', (f'{class_name_prefix}%', limit))\n    #print(\"5\")\n    results = cursor.fetchall()\n    if results:\n        return results\n    cursor.execute('''\n        SELECT class_package_name, class_name, readable_proto_list FROM app_methods\n        WHERE class_package_name LIKE ?\n        LIMIT ?\n    ''', (f'%{class_name_prefix}%', limit))\n    #print(\"6\")\n    results = cursor.fetchall()\n    if results:\n        return results\n    if not \".\" in class_name_prefix:\n        return []\n    class_name_prefix, class_name = class_name_prefix.rsplit(\".\", 1)\n    return query_class_name_by_prefix(class_name_prefix, class_name, limit)\n\ndef get_need_to_cache_pkg_prefix():\n    results = {\"okhttp3\", \"retrofit2\", \"javax.crypto\", \"java.security\"}\n    try:\n        logging.getLogger(\"androguard.core.api_specific_resources\").setLevel(logging.ERROR)\n        a = apk.APK(current_local_apk_path)\n        activities = a.get_activities()\n        # 取每个activity包名前两段\n        prefixes = []\n        for activity in activities:\n            parts = activity.split('.')\n            if len(parts) >= 2:\n                prefix = '.'.join(parts[:2])\n            else:\n                prefix = activity\n            prefixes.append(prefix)\n        # 统计出现次数\n        counter = Counter(prefixes)\n        # 找出现次数最多的前两个\n        most_common_two = counter.most_common(2)\n        for prefix, count in most_common_two:\n            results.add(prefix)\n        package_name = current_identifier    \n        parts = package_name.split('.')\n        results.add('.'.join(parts[:2]) if len(parts) >= 2 else package_name)\n        #info(f\"need_to_cache_pkg_prefix:{results}\")\n    except Exception as e:\n        return []\n    return list(results)\n\ndef load_dexes_to_cache():\n    if not zipfile.is_zipfile(current_local_apk_path):\n        warn(f\"{current_local_apk_path} is not a legal zip file\")\n        return\n    open_or_create_db()\n    def process_dex():\n        need_to_cache_pkg_prefix = get_need_to_cache_pkg_prefix()\n        with zipfile.ZipFile(current_local_apk_path, 'r') as zip_ref:\n            for file_info in zip_ref.infolist():\n                if file_info.filename.endswith('.dex'):\n                    with zip_ref.open(file_info.filename) as dex_file:\n                        dex_data = dex_file.read()  # 读取为 bytes\n                        load_classes_and_methods_to_db(file_info.filename, dex_data, need_to_cache_pkg_prefix)\n                        if current_identifier_stop_event == None or current_identifier_stop_event.is_set():\n                            # info(\"中断线程3\")\n                            break\n                        if count_methods_by_app_version() > 300000:\n                            break\n    global current_identifier_stop_event\n    current_identifier_stop_event = threading.Event()\n    t = threading.Thread(target=process_dex)\n    t.daemon = True\n    t.start()\n    \ndef load_classes_and_methods_to_db(filename, dex_bytes, need_to_cache_pkg_prefix):\n    if not current_identifier_cache_db:\n        return\n    if current_identifier_stop_event == None or current_identifier_stop_event.is_set():\n        return\n    cursor = current_identifier_cache_db.cursor()\n    need_to_cache_pkg = tuple(need_to_cache_pkg_prefix)\n    obfuscated_package_cahce = set()\n    def is_obfuscated_package(pkg_name: str) -> bool:\n        if pkg_name in obfuscated_package_cahce:\n            return True\n        parts = pkg_name.split(\".\")\n        if len(parts) > 3 or (len(parts) == 1 and len(parts[0]) > 3):\n            return False\n        if len(parts) < 2 and len(parts[0]) <= 3:\n            obfuscated_package_cahce.add(pkg_name)\n            #info(f\"混淆包名1:{class_package_name}\")\n            return True\n        short_count = sum(1 for part in parts if len(part) <= 1)\n        upper_count = sum(1 for part in parts if part.isupper())\n        if short_count >= len(parts) // 2:\n            obfuscated_package_cahce.add(pkg_name)\n            #info(f\"混淆包名2:{class_package_name}\")\n            return True\n        if upper_count >= len(parts) // 2:\n            obfuscated_package_cahce.add(pkg_name)\n            #info(f\"混淆包名3:{class_package_name}\")\n            return True\n        return False\n    def should_skip_class_package(class_package_name):\n        if is_obfuscated_package(class_package_name):\n            return False\n        if not class_package_name.startswith(need_to_cache_pkg):\n            return True\n        return False\n    loaded_count = 0\n    try:\n        dex = dvm.DalvikVMFormat(dex_bytes)\n        for cls in dex.get_classes():\n            if loaded_count > 2000:\n                #info(f\"already loaded dex {filename}\")\n                return\n            access_flags = cls.get_access_flags()\n            # （0x200 = ACC_INTERFACE）（0x400 = ACC_ABSTRACT）\n            if (access_flags & 0x200) or (access_flags & 0x400):\n                continue\n            if current_identifier_stop_event == None or current_identifier_stop_event.is_set():\n                # info(\"中断线程\")\n                return\n            class_name = cls.get_name().strip('L;').replace('/', '.')\n            temp = class_name.rsplit(\".\", 1)\n            class_package_name = temp[0]\n            if len(temp) != 2 or should_skip_class_package(class_package_name):\n                continue\n            class_name = temp[1]\n            readable_proto_list = \"\"\n            for method in cls.get_methods():\n                method_name = method.get_name()\n                proto = method.get_descriptor()  # e.g., (Ljava/lang/String;)V\n                # 转换描述符为更易读格式\n                readable_proto = convert_descriptor_to_readable(proto)\n                readable_proto_list += f\"|{method_name}{readable_proto}\"\n            already_exists = insert_if_not_exists(cursor, filename, class_package_name, class_name, \"\", readable_proto_list)\n            if already_exists:\n                loaded_count += 1\n    except Exception as e:\n        pass\n        #print(traceback.format_exc())\n        #print(f\"[!] Error processing {filename}: {e}\")\n\ndef convert_descriptor_to_readable(descriptor):\n    def type_map(d):\n        mapping = {\n            'I': 'int', 'Z': 'boolean', 'B': 'byte',\n            'S': 'short', 'J': 'long', 'F': 'float',\n            'D': 'double', 'C': 'char', 'V': 'void',\n        }\n        if d.startswith('L') and d.endswith(';'):\n            return d[1:-1].replace('/', '.').split('.')[-1]\n        return mapping.get(d, d)\n    args, ret = descriptor.split(')')\n    args = args[1:]  # remove opening '('\n    i = 0\n    parsed = []\n    while i < len(args):\n        if args[i] == 'L':\n            j = i\n            while args[j] != ';':\n                j += 1\n            parsed.append(type_map(args[i:j+1]))\n            i = j + 1\n        elif args[i] in \"ZBSCIJFD\":\n            parsed.append(type_map(args[i]))\n            i += 1\n        elif args[i] == '[':\n            dim = 0\n            while args[i] == '[':\n                dim += 1\n                i += 1\n            if args[i] == 'L':\n                j = i\n                while args[j] != ';':\n                    j += 1\n                base = type_map(args[i:j+1])\n                i = j + 1\n            else:\n                base = type_map(args[i])\n                i += 1\n            parsed.append(base + '[]' * dim)\n        else:\n            i += 1  # unknown type, skip\n    return f\"({', '.join(parsed)})\"\n\ndef push_file_to_device_with_chmod(local_file, remote_file = None):\n    filename = local_file.split(\"/\")[-1]\n    if remote_file == None:\n        remote_file = f\"/data/user/0/{current_identifier}/{filename}\"\n    compara_and_update_file(f\"{current_identifier}/{local_file}\", remote_file)\n    user_group_id = f\"u0_a{(int(current_identifier_uid) - 10000)}\"\n    run_su_command(f\"chown {user_group_id}:{user_group_id} {remote_file}\")\n    run_su_command(f\"chmod 777 {remote_file}\")\n    info(f\"push file OK {remote_file}\")\n    return remote_file\n\ndef start_web_server(jar_file:str = \"\", with_xposed_daemon = False):\n    remote_dex_file = \"\"\n    all_classes = []\n    if jar_file:\n        dex_file = convert_jar_to_dex(jar_file)\n        with open(f\"{current_identifier}/{dex_file}\", \"rb\") as f:\n            dex = dvm.DalvikVMFormat(f.read())\n        all_classes = []\n        for cls in dex.get_classes():\n            # 判断是否接口、抽象类、注解\n            access_flags = cls.get_access_flags()\n            # （0x200 = ACC_INTERFACE）（0x400 = ACC_ABSTRACT）（0x2000 = ACC_ANNOTATION）\n            if (access_flags & 0x200) or (access_flags & 0x400) or (access_flags & 0x2000):\n                continue\n            class_name = cls.get_name()[1:-1].replace(\"/\", \".\")\n            all_classes.append(class_name)\n        if len(all_classes) == 0:\n            warn(f\"Deploy failure. not found any class in {jar_file}\")\n            return\n        remote_file = f\"/data/user/0/{current_identifier}/hooker_server.dex\"\n        remote_dex_file = push_file_to_device_with_chmod(dex_file, remote_file)\n    rpc_start_web_server(remote_dex_file, all_classes)\n\ndef stop_web_server():\n    cmd = \"curl --max-time 3 \" + webserver_url + \"/stop\"\n    result = adb_device.shell(cmd)\n    info(result)\n\ndef tail_android_file(filepath: str):\n    \"\"\"\n    通过 adb 以 tail -f 方式实时读取安卓设备上的文件。\n    Ctrl + C 可安全退出。\n    \"\"\"\n    if not check_remote_file_exists(filepath):\n        info(\"There is no log yet\")\n        return\n    info(f\"viewloging\")\n    cmd = [\"adb\", \"shell\", \"tail\", \"-f\", filepath]\n    # 启动 adb 进程\n    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)\n    try:\n        for line in p.stdout:\n            if not line:\n                continue\n            line = line.rstrip()\n            if \"[warn]\" in line or \"[WARN]\" in line:\n                info(line)\n            elif \"[error]\" in line or \"[ERROR]\" in line:\n                warn(line)\n            else:\n                print(line)\n    except KeyboardInterrupt:\n        print(\"\\nCtrl + C received, stopping tail...\")\n    finally:\n        p.terminate()     # 结束 tail 进程\n        try:\n            p.wait(timeout=2)\n        except subprocess.TimeoutExpired:\n            p.kill()\n        print(\"viewlog stopped.\")\n\nclass ClassNameCompleter(Completer):\n    def __init__(self):\n        js_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".js\")\n        }\n        pushable_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".dex\") or filename.endswith(\".so\") or filename.endswith(\".jpg\")\n        }\n        jar_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".jar\")\n        }\n        viewlog = {\n            \"webserver\": None,\n        }\n        output = adb_device.shell(f\"find {current_identifier_install_path}/lib/ -type f\")\n        self.so_files = {\n            path.split('/')[-1]: path\n            for path in output.strip().split(\"\\n\")\n            if path.endswith(\".so\")\n        }\n        self.nested_dict = {\n            'help': None,\n            'h': None,\n            'activitys': None,\n            'a': None,\n            'services': None,\n            's': None,\n            'object': None,\n            'o': None,\n            'view': None,\n            'v': None,\n            'generatescript': None,\n            'gs': None,\n            'proxy': {\"socks5://\": None},\n            'p': {\"socks5://\": None},\n            'unproxy': None,\n            'up': None,\n            'justtrustme': None,\n            'trust': None,\n            'r0capture': None,\n            'ls': None,\n            'push': pushable_files,\n            'attach': js_files,\n            'frida': js_files,\n            'spawn': js_files,\n            'fridaf': js_files,\n            'restart': None,\n            'pid': None,\n            'uid': None,\n            'pull': None,\n            'webserver': {\n                \"start\": jar_files,\n                \"stop\": None,\n            },\n            'viewlog': viewlog,\n            'exit': None,\n        }\n        self.debug_completer = NestedCompleter.from_nested_dict(self.nested_dict)\n        \n    def update_js_files(self):\n        js_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".js\")\n        }\n        pushable_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".dex\") or filename.endswith(\".so\") or filename.endswith(\".jpg\")\n        }\n        jar_files = {\n            filename: None\n            for filename in os.listdir(current_identifier)\n            if filename.endswith(\".jar\")\n        }\n        self.nested_dict[\"attach\"] = js_files\n        self.nested_dict[\"frida\"] = js_files\n        self.nested_dict[\"spawn\"] = js_files\n        self.nested_dict[\"fridaf\"] = js_files\n        self.nested_dict[\"push\"] = pushable_files\n        self.nested_dict[\"webserver\"] = {\n            \"start\": jar_files,\n            \"stop\": None,\n        }\n        self.debug_completer = NestedCompleter.from_nested_dict(self.nested_dict)\n        \n    def get_completions(self, document, complete_event):\n        text = document.text_before_cursor.strip()\n        #print(\"\\nget_completions:\"+text)\n        generatescript_cmd_match = re.search(r\"(generatescript|gs)\\s+([^\\s]+)\", text)\n        if generatescript_cmd_match:\n            try:\n                value = generatescript_cmd_match.group(2)\n                max_items = 15\n                count = 0\n                full_class_name = None\n                class_name_prefix = None\n                class_name = None\n                method_prefix = None\n                if \":\" in value:\n                    full_class_name, method_prefix = value.split(\":\", 1)\n                else:\n                    full_class_name = value\n                class_name_prefix = full_class_name\n                # print(\"\\nclass_name_prefix:\"+class_name_prefix)\n                results = query_class_name_by_prefix(class_name_prefix, class_name, limit=max_items)\n                # print(f\"{len(results)}\")\n                if len(results) == 0:\n                    #info(f\"query_class_name_by_prefix class_name_prefix:{class_name_prefix} class_name:{class_name}\")\n                    pass\n                for row in results:\n                    count += 1\n                    class_package_name = row[0]\n                    class_name = row[1]\n                    yield Completion(f\"{class_package_name}.{class_name}\", start_position=-len(value))\n                if count >= max_items:\n                    return\n                for row in results:\n                    class_package_name = row[0]\n                    class_name = row[1]\n                    readable_proto_list = row[2]\n                    readable_proto_mehtod_list = readable_proto_list.split(\"|\")[1:]\n                    for readable_proto_mehtod in readable_proto_mehtod_list:\n                        count += 1\n                        yield Completion(f\"{class_package_name}.{class_name}:{readable_proto_mehtod}\", start_position=-len(value))\n                        if count > max_items:\n                            return\n            except Exception as e:\n                traceback.print_exc()\n                #yield Completion(f\"[ERROR: {e}]\", start_position=0)\n                pass\n        pull_cmd_match = re.search(r\"pull\\s+([^\\s]+)\", text)\n        if pull_cmd_match:\n            filepath = pull_cmd_match.group(1)\n            for so_name, so_path in self.so_files.items():\n                if filepath in so_path:\n                    yield Completion(so_path, start_position=-len(filepath))\n        else:\n            # 其他命令提示\n            for c in self.debug_completer.get_completions(document, complete_event):\n                yield c\n                        \ncmd_session = PromptSession()\nclassNameCompleter = None\n    \ndef entry_debug_mode():    \n    def handle_command(cmd):\n        cmd = cmd.strip()\n        if cmd.startswith(\"activitys\") or \"a\" == cmd:\n            print_activitys()\n            return True\n        elif cmd.startswith(\"services\") or \"s\" == cmd:\n            print_services()\n            return True\n        elif (cmd.startswith(\"object \") or cmd.startswith(\"o \")) and re.search(r\"(object|o)\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"(object|o)\\s+([^\\s]+)\", cmd)\n            if m:\n                print_object(m.group(2))\n                return True\n        elif (cmd.startswith(\"view \") or cmd.startswith(\"v \")) and re.search(r\"(view|v)\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"(view|v)\\s+([^\\s]+)\", cmd)\n            if m:\n                print_view(m.group(2))\n                return True\n        elif cmd == \"ls\":\n            list_working_dir()\n            classNameCompleter.update_js_files()\n            return True\n        elif cmd == \"justtrustme\" or cmd == \"trust\":\n            just_trust_me()\n            return True\n        elif cmd.startswith(\"push \") and re.search(r\"push\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"push\\s+([^\\s]+)\", cmd)\n            if m:\n                local_file = m.group(1)\n                m2 = re.search(r\"push\\s+[^\\s]+\\s+([^\\s]+)\", cmd)\n                if m2 is not None:\n                    remote_file = m2.group(1)\n                    push_file_to_device_with_chmod(local_file, remote_file)\n                else:\n                    push_file_to_device_with_chmod(local_file)\n            return True\n        elif re.search(r\"webserver\\s+start\", cmd):\n            m = re.search(r\"webserver\\s+start\\s+([^\\s]+\\.jar)\", cmd)\n            if m:\n                jar_file = m.group(1)\n                start_web_server(jar_file)\n            else:\n                start_web_server()\n            return True\n        elif re.search(r\"webserver\\s+stop\", cmd):\n            m = re.search(r\"webserver\\s+stop\\s+([^\\d]+)\", cmd)\n            if m:\n                port = m.group(1)\n                stop_web_server(int(port))\n            else:\n                stop_web_server()\n            return True\n        elif cmd.startswith(\"viewlog\") or re.search(r\"viewlog\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"webserver\\s+([^\\s]+)\", cmd)\n            if m:\n                device_log_file = m.group(1)\n                if device_log_file == \"webserver\":\n                    tail_android_file(\"/sdcard/webserver.log\")\n                else:\n                    tail_android_file(device_log_file)\n            else:\n                tail_android_file(\"/sdcard/webserver.log\")\n            return True\n        elif cmd == \"r0capture\":\n            r0capture()\n            return True\n        elif (cmd.startswith(\"proxy \") or cmd.startswith(\"p \")) and re.search(r\"(proxy|p)\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"(proxy|p)\\s+([^\\s]+)\", cmd)\n            if m:\n                set_proxy(m.group(2))\n                return True\n        elif cmd == \"unproxy\" or cmd == \"up\":\n            un_proxy()\n            info(\"un_proxy OK\")\n            return True\n        elif (cmd.startswith(\"attach \") or cmd.startswith(\"frida \")) and re.search(r\"(attach|frida)\\s+([^\\s]+\\.js)\", cmd):\n            m = re.search(r\"(attach|frida)\\s+([^\\s]+)\", cmd)\n            if m:\n                execute_script(m.group(2), False)\n                return True\n        elif (cmd.startswith(\"spawn \") or cmd.startswith(\"fridaf \")) and re.search(r\"(spawn|fridaf)\\s+([^\\s]+\\.js)\", cmd):\n            m = re.search(r\"(spawn|fridaf)\\s+([^\\s]+)\", cmd)\n            if m:\n                execute_script(m.group(2), True)\n                return True\n        elif cmd == \"restart\":\n            restart_app(current_identifier)\n            return True\n        elif cmd == \"pid\":\n            info(current_identifier_pid)\n            return True\n        elif cmd == \"uid\":\n            info(current_identifier_uid)\n            return True\n        elif cmd.startswith(\"pull \") and re.search(r\"pull\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"pull\\s+([^\\s]+)\", cmd)\n            if m:\n                path = m.group(1)\n                filename = path.split('/')[-1]\n                pull_file_to_local(path, f\"{current_identifier}/{filename}\", True)\n                return True\n        elif (cmd.startswith(\"generatescript \") or cmd.startswith(\"gs \")) and re.search(r\"(generatescript|gs)\\s+([^\\s]+)\", cmd):\n            m = re.search(r\"(generatescript|gs)\\s+([^\\s]+)\", cmd)\n            if m:\n                info(\"Generating frida script, please wait for a few seconds\")\n                hook_js(m.group(2), None)\n                classNameCompleter.update_js_files()\n            else:\n                warn(f\"Can not parse class and method: {cmd}\")\n            return True\n        return False\n    help_msg = [\n        (\"h, help\", \"show this help message\"),\n        (\"a, activitys\", \"show the activity stack\"),\n        (\"s, services\", \"show the service stack\"),\n        (\"o, object [object_id]\", \"show object info by object_id or classname.\"),\n        (\"v, view [view_id]\", \"show view info by view_id of view\"),\n        (\"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\"),\n        (\"p, proxy [socks5_proxy_server]\", \"set up a socks5 proxy for this app. For example: proxy socks5://192.168.0.100:9998\"),\n        (\"up, unproxy\", \"remove socks5 proxy for this app\"),\n        (\"trust, justtrustme\", \"quickly spawn just_trust_me.js script to kill all ssl pinning\"),\n        (\"r0capture\", \"quickly spawn r0capture.js script to capture ssl/tls packages\"),\n        (\"ls\", \"list all the frida scripts of the current app\"),\n        (\"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\"),\n        (\"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\"),\n        (\"restart\", \"restart this app\"),\n        (\"pid\", \"get pid of this app main process\"),\n        (\"uid\", \"get uid of this app\"),\n        (\"pull\", \"quickly pull a file to the local application's working directory with a filepath or so filename. For example: pull libmsaoaidsec.so\"),\n        (\"push\",\n         \"quickly push a file to mobile storage with specify path. eg: push example-patch.dex\"),\n        (\"webserver [start/stop] [controller.jar/8080]\",\n         \"quickly start or atop a webserver with a jar file developed by radar4hooker eg: webserver start\"),\n        (\"exit\", \"return to the previous level\"),\n    ]\n    def print_help_msg():\n        GREEN = \"\\033[32m\"\n        YELLOW = \"\\033[33m\"\n        RESET = \"\\033[0m\"\n        # 获取终端宽度，默认宽度 80\n        term_width = shutil.get_terminal_size((80, 20)).columns\n        max_cmd_len = max(len(cmd) for cmd, _ in help_msg) + 2\n        for cmd, desc in help_msg:\n            cmd_part = f\"{GREEN}{cmd.ljust(max_cmd_len)}{RESET}\"\n            desc_lines = textwrap.wrap(desc, width=term_width - max_cmd_len)\n            if desc_lines:\n                print(cmd_part + f\"{YELLOW}{desc_lines[0]}{RESET}\")\n                for line in desc_lines[1:]:\n                    print(\" \" * max_cmd_len + f\"{YELLOW}{line}{RESET}\")\n            else:\n                print(cmd_part)\n    hooker_cmd = \"\"\n    list_working_dir()\n    classNameCompleter = ClassNameCompleter()\n    while True:\n        try:\n            hooker_cmd = cmd_session.prompt(f'{current_identifier_name} > ', completer=classNameCompleter)\n            hooker_cmd = hooker_cmd.strip()\n            if hooker_cmd == 'exit' or hooker_cmd == 'quit':\n                break\n            if hooker_cmd == 'h' or hooker_cmd == 'help':\n                print_help_msg()\n                continue\n            is_handled = handle_command(hooker_cmd)\n            if not is_handled and hooker_cmd:\n                info(f\"hooker command not found: {hooker_cmd} Please enter \\\"help\\\" + Enter to view the help information\")\n                continue\n            elif not hooker_cmd:\n                continue\n        except (EOFError, KeyboardInterrupt):\n            break        \n\n\n\ndef pad_display(text, width):\n    \"\"\"按显示宽度对齐文本\"\"\"\n    text = str(text)\n    padding = width - wcswidth(text)\n    return text + ' ' * max(padding, 0)\n\ndef list_third_party_apps():\n    identifier_list = []\n    apps = enumerate_applications_adbutils(False, True)\n    print(f\"{pad_display('PID', 6)}\\t{pad_display('APP', 20)}\\t{pad_display('IDENTIFIER', 35)}\\tEXIST_REVERSE_DIRECTORY\")\n    for app in sorted(apps, key=lambda x: x.pid or 0):\n        if app.pid is not None:  # 只列出运行中的\n            reverse_directory_exist = os.path.isdir(app.identifier)\n            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 '❌'}\")\n            identifier_list.append(app.identifier)\n    return identifier_list\n\ndef upgrade():\n    info(\"Upgrading hooker\")\n    repo_url = \"https://github.com/CreditTone/hooker.git\"\n    upgrade_dir = \"./.upgrade_hooker\"\n    if os.path.exists(upgrade_dir):\n        # 目录存在，执行 git pull\n        try:\n            repo = Repo(upgrade_dir)\n            origin = repo.remotes.origin\n            origin.pull()\n            info(\"Repository updated with 'git pull'.\")\n        except Exception as e:\n            info(f\"Failed to update repository: {e}\")\n    else:\n        # 目录不存在，执行 git clone\n        Repo.clone_from(repo_url, upgrade_dir)\n        info(\"Repository cloned.\")\n    def copy_if_different(a: str, b: str):\n        \"\"\"\n        如果文件 b 不存在，或者 a 和 b 内容不同，\n        则把 a 覆盖复制到 b。\n        \"\"\"\n        # 如果 b 不存在，直接复制\n        if not os.path.exists(b):\n            shutil.copy2(a, b)\n            info(f\"Updating {b}\")\n            return\n        # 如果 a 和 b 内容相同，不做操作\n        if filecmp.cmp(a, b, shallow=False):\n            #info(f\"a 和 b 内容相同，不做操作 {a} {b}\")\n            return\n        # 内容不同，复制\n        shutil.copy2(a, b)\n        info(f\"Updating {b}\")\n    def update_dir_files(remote_dir, local_dir):\n        for root, dirs, files in os.walk(remote_dir):\n            for file in files:\n                file_path = os.path.join(root, file)\n                copy_if_different(file_path, f\"{local_dir}/{file}\")\n    update_dir_files(f\"{upgrade_dir}/js\", \"js\")\n    update_dir_files(f\"{upgrade_dir}/mobile-deploy\", \"mobile-deploy\")\n    copy_if_different(f\"{upgrade_dir}/hooker.py\", \"hooker.py\")\n    copy_if_different(f\"{upgrade_dir}/README.md\", \"README.md\")\n    copy_if_different(f\"{upgrade_dir}/README_EN.md\", \"README_EN.md\")\n    shutil.rmtree(upgrade_dir)\n    info('Please restart hooker')\n    sys.exit(2);\n    \nif len(sys.argv) > 1:\n    arg = sys.argv[1]\n    if arg == \"upgrade\":\n        upgrade()\n    \nwhile True:\n    try:\n        info(\"hooker Let's enjoy reverse engineering together\")\n        info(\"-----------------------------------------------------------------------------------------------\")\n        first_command_list = list_third_party_apps()\n        first_command_list.append(\"exit\")\n        first_command_list.append(\"quit\")\n        first_command_list.append(\"upgrade\")\n        print(\"Please enter the identifier that needs to be reversed\")\n        identifier = cmd_session.prompt('hooker(Identifier): ', completer=WordCompleter(first_command_list, ignore_case=False, match_middle=True, WORD=True))\n        identifier = identifier.strip()\n        if identifier == 'exit' or identifier == 'exit()' or identifier == 'quit':\n            info('ByeBye!')\n            sys.exit(2);\n            break\n        if identifier == 'upgrade':\n            upgrade()\n            break\n        if identifier not in first_command_list:\n            warn(\"The application does not exist. Please enter an existing application\")\n            continue\n        current_identifier = identifier\n        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)\n        if not os.path.isdir(identifier):\n            create_working_dir_enverment()\n        else:\n            init_working_dir_enverment()\n        load_dexes_to_cache()\n        check_dependency_files()\n        info(f\"current working directory: hooker/{current_identifier}\")\n        entry_debug_mode()\n        # 从debug模式跳出来\n        current_identifier = None\n        current_identifier_name = None\n        current_identifier_version = None\n        current_identifier_pid = None\n        current_identifier_install_path = None\n        current_identifier_uid = None\n        current_local_apk_path = None\n        current_identifier_cache_db = None\n        current_identifier_cache_readonly_db = None\n        if current_identifier_stop_event:\n            current_identifier_stop_event.set()\n        # current_identifier_stop_event = None\n    except (EOFError, KeyboardInterrupt):\n        sys.exit(2);\n    \n\n"
  },
  {
    "path": "js/_hook_js_enhance.js",
    "content": "function check_load_dex(className, dexfile) {\n    Java.perform(function() {\n        if (!classExists(className)) {\n            Java.openClassFile(dexfile).load();\n            //console.log(\"load \" + dexfile);\n        }\n    });\n};\n\nfunction class_exists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nfunction get_class_name(obj) {\n    if (obj.getClass) {\n        return obj.getClass().getName();\n    }\n    var javaObject = Java.use(\"java.lang.Object\");\n    return Java.cast(obj, javaObject).getClass().getName();\n}\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while(new Date().getTime() < startTime) {}\n};\n\nfunction load_radar_dexfile() {\n    loaded_radar_dex_flag = true;\n    Java.openClassFile(dexfile).load();\n};\n\nfunction fast_to_json(javaObject) {\n    if (!loaded_radar_dex_flag){\n        load_radar_dexfile();\n    }\n    var JSONClz = Java.use(\"gz.com.alibaba.fastjson.JSON\");\n    return JSONClz.toJSONString(javaObject);\n};\n\nfunction get_pretty_string(javaObject) {\n    if (!loaded_radar_dex_flag){\n        load_radar_dexfile();\n    }\n    var XPretty = Java.use(\"gz.util.XPretty\");\n    return XPretty.getPrettyString(javaObject);\n};\n\n\nfunction get_object_field_object(javaObject, fieldName) {\n    if (!loaded_radar_dex_flag){\n        load_radar_dexfile();\n    }\n    var X = Java.use(\"gz.util.X\");\n    return X.getField(javaObject, fieldName);\n};\n\nfunction store_object(javaObject) {\n    if (!loaded_radar_dex_flag){\n        load_radar_dexfile();\n    }\n    try {\n        var className = getClassName(javaObject);\n        var ObjectsStore = Java.use(\"gz.radar.objects.ObjectsStore\");\n        var objectId = ObjectsStore.storeObject(javaObject);\n        console.log(className + \" ObjectsStoreId: \" +objectId);\n    } catch (error) {\n        console.error(error);\n    }\n};\n\n// 当Okhttp3Request对象是post的时候，你读取body会消耗一次，使后面的请求不成功，这时候我们就要克隆一个新的Request\nfunction printAndCloneOkhttp3Request(ok3ReqObj) {\n    var logObj = {};\n    // 类名\n    logObj.class = ok3ReqObj.getClass().getName();\n    // URL\n    logObj.url = ok3ReqObj.url().toString();\n    // 方法\n    logObj.method = ok3ReqObj.method();\n    // 请求头\n    var headers = {};\n    var headerList = ok3ReqObj.headers();\n    for (var i = 0; i < headerList.size(); i++) {\n        headers[headerList.name(i)] = headerList.value(i);\n    }\n    logObj.headers = headers;\n    // Tag\n    var tag = ok3ReqObj.tag();\n    logObj.tag = tag ? tag.toString() : null;\n    // 请求体克隆\n    var body = ok3ReqObj.body();\n    var newRequest = null;\n    var bodyContent = null;\n    if (body) {\n        var BufferClz = Java.use(\"okio.Buffer\");\n        var buffer = BufferClz.$new();\n        body.writeTo(buffer);  // 第一次读取到流\n        bodyContent = buffer.readUtf8();  // 保存内容\n        var RequestBodyClz = Java.use(\"okhttp3.RequestBody\");\n        var newBody = RequestBodyClz.create(body.contentType(), bodyContent);\n        // 克隆新请求体\n        newRequest = ok3ReqObj.newBuilder()\n            .method(ok3ReqObj.method(), newBody)\n            .build();\n    } else {\n        newRequest = ok3ReqObj.newBuilder().build();\n    }\n    logObj.body = bodyContent;\n    // 打印 JSON 格式\n    console.log(JSON.stringify(logObj, null, 4));\n    return newRequest;\n}\n\n// Response读取body会消耗一次，使后面的程序读取不成功，这时候我们就要克隆一个新的Response\nfunction printAndCloneOkhttp3Response(ok3ResObj) {\n    // 构建 JSON 数据\n    var result = {\n        request: {},\n        response: {}\n    };\n\n    // 获取 Request 信息\n    var request = ok3ResObj.request();\n    result.request.url = request.url().toString();\n    result.request.method = request.method();\n\n    // 请求头\n    var reqHeaders = request.headers();\n    var reqHeadersJson = {};\n    for (var i = 0; i < reqHeaders.size(); i++) {\n        reqHeadersJson[reqHeaders.name(i)] = reqHeaders.value(i);\n    }\n    result.request.headers = reqHeadersJson;\n\n    // 获取 Response 信息\n    result.response.statusCode = ok3ResObj.code();\n\n    var resHeaders = ok3ResObj.headers();\n    var resHeadersJson = {};\n    for (var i = 0; i < resHeaders.size(); i++) {\n        resHeadersJson[resHeaders.name(i)] = resHeaders.value(i);\n    }\n    result.response.headers = resHeadersJson;\n    var newOk3ResObj = ok3ResObj;\n    // 读取 Response Body\n    var body = ok3ResObj.body();\n    if (body) {\n        try {\n            var bodyStr = body.string();\n            result.response.body = bodyStr;\n\n            // 重新封装 Body 防止内容被消耗\n            var newBody = Java.use(\"okhttp3.ResponseBody\").create(body.contentType(), Java.use(\"java.lang.String\").$new(bodyStr));\n            newOk3ResObj = ok3ResObj.newBuilder().body(newBody).build();\n        } catch (e) {\n            result.response.body = \"[!] Failed to read body: \" + e;\n        }\n    } else {\n        result.response.body = \"[!] No body\";\n    }\n\n    // 将 JSON 数据格式化输出\n    console.log(JSON.stringify(result, null, 4));\n    return newOk3ResObj\n}\n\n"
  },
  {
    "path": "js/_hook_js_prepare.js",
    "content": "var loaded_radar_dex_flag = false;\nvar dexfile = \"/data/local/tmp/radar.dex\";\n\nfunction newMethodBeat(text, executor) {\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var processClz = Java.use(\"android.os.Process\");\n    var currentThread = threadClz.currentThread();\n    var beat = new Object();\n    beat.invokeId = Math.random().toString(36).slice( - 8);\n    beat.executor = executor;\n    beat.myPid = processClz.myPid();\n    beat.threadId = currentThread.getId();\n    beat.threadName = currentThread.getName();\n    beat.text = text;\n    beat.startTime = new Date().getTime();\n    beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20);\n    return beat;\n};\n\nfunction printBeat(beat) {\n    var str = (\"------------pid:\" + beat.myPid + \",startFlag:\" + beat.invokeId + \",objectHash:\"+beat.executor+\",thread(id:\" + beat.threadId +\",name:\" + beat.threadName + \"),timestamp:\" + beat.startTime+\"---------------\\n\");\n    str += beat.text + \"\\n\";\n    str += beat.stackInfo;\n    str += (\"------------endFlag:\" + beat.invokeId + \",usedtime:\" + (new Date().getTime() - beat.startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n"
  },
  {
    "path": "js/_hook_js_warp.js",
    "content": "rpc.exports = {\n    cleanup: function () {\n        // 清理所有拦截器\n        Interceptor.detachAll();\n        // 如果你设置了定时器或 Stalker，也可以清理\n        Stalker.unfollow();\n        // clearInterval(...);\n    }\n};"
  },
  {
    "path": "js/activity_events.js",
    "content": "function loadDexfile(dexfile) {\n    Java.perform(function() {\n          Java.openClassFile(dexfile).load();\n          //console.log(\"load \" + dexfile);\n    });\n};\n\nfunction checkLoadDex(className, dexfile) {\n    Java.perform(function() {\n        if (!classExists(className)) {\n            Java.openClassFile(dexfile).load();\n            //console.log(\"load \" + dexfile);\n        }\n    });\n};\n\nfunction classExists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nfunction getClassName(obj) {\n    if (obj.getClass) {\n        return obj.getClass().getName();\n    }\n    var javaObject = Java.use(\"java.lang.Object\");\n    return Java.cast(obj, javaObject).getClass().getName();\n}\n\n//str1是否包含str2，str2可用正则表示\nfunction contains(str1, str2) {\n    var reg = RegExp(eval(\"/\"+str2+\"/\"));\n    if(str1 && str1.match && str1.match(reg)){\n        return true;\n    }else{\n        return false;\n    }\n};\n\n//创建ArrayList对象用这个方法就好了\nfunction newArrayList() {\n    var ArrayListClz = Java.use('java.util.ArrayList');\n    return ArrayListClz.$new();\n}\n\n//创建HashSet对象用这个方法就好了\nfunction newHashSet() {\n    var HashSetClz = Java.use('java.util.HashSet');\n    return HashSetClz.$new();\n}\n\n//创建HashMap对象用这个方法就好了\nfunction newHashMap() {\n    var HashMapClz = Java.use('java.util.HashMap');\n    return HashMapClz.$new();\n}\n\nfunction newMethodBeat(text, executor) {\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var currentThread = threadClz.currentThread();\n    var beat = new Object();\n    beat.invokeId = Math.random().toString(36).slice( - 8);\n    beat.executor = executor;\n    beat.threadId = currentThread.getId();\n    beat.threadName = currentThread.getName();\n    beat.text = text;\n    beat.startTime = new Date().getTime();\n    beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20);\n    return beat;\n};\n\nfunction printBeat(beat) {\n    var str = (\"------------startFlag:\" + beat.invokeId + \",objectHash:\"+beat.executor+\",thread(id:\" + beat.threadId +\",name:\" + beat.threadName + \"),timestamp:\" + beat.startTime+\"---------------\\n\");\n    str += beat.text + \"\\n\";\n    str += beat.stackInfo;\n    str += (\"------------endFlag:\" + beat.invokeId + \",usedtime:\" + (new Date().getTime() - beat.startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nfunction log(str) {\n    console.log(str);\n};\n\n//虽然我们习惯用fastjson一行将对象转成json字符串，但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来，很多人不知道。在frida中所有代码都是透明的，你随便调......\nfunction toJson(javaObject) {\n    var gsonClz = Java.use(\"com.google.gson.Gson\");\n    var toJsonMethod = gsonClz.toJson.overload(\"java.lang.Object\");\n    return toJsonMethod.call(gsonClz.$new(),javaObject);\n};\n\nfunction getBaseContext() {\n    var currentApplication = Java.use('android.app.ActivityThread').currentApplication();\n    var context = currentApplication.getApplicationContext();\n    return context; //Java.scheduleOnMainThread(fn):\n};\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while(new Date().getTime() < startTime) {}\n};\n\nfunction fastTojson(javaObject) {\n    var JSONClz = Java.use(\"gz.com.alibaba.fastjson.JSON\");\n    return JSONClz.toJSONString(javaObject);\n};\n\nloadDexfile('/data/local/tmp/radar.dex');\n\nJava.perform(function() {\n    var radarAndroidClz = Java.use(\"gz.radar.Android\");\n    var android_content_ContextWrapper_clz = Java.use('android.content.ContextWrapper');\n    var android_content_ContextWrapper_clz_method_startActivity_r7jq = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent', 'android.os.Bundle');\n    android_content_ContextWrapper_clz_method_startActivity_r7jq.implementation = function(v0, v1) { \n        log(\"Intent>>>>>>>\"+radarAndroidClz.getIntentProfile(v0));\n        log(\"Bundle>>>>>>>\"+radarAndroidClz.getBundleProfile(v1));\n        var executor = this.hashCode();\n        var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent,android.os.Bundle)';\n        var beat = newMethodBeat(beatText, executor);\n        android_content_ContextWrapper_clz_method_startActivity_r7jq.call(this, v0, v1);\n        printBeat(beat);\n    };\n    var android_content_ContextWrapper_clz_method_startActivity_auep = android_content_ContextWrapper_clz.startActivity.overload('android.content.Intent');\n    android_content_ContextWrapper_clz_method_startActivity_auep.implementation = function(v0) {\n        log(\"Intent>>>>>>>\"+radarAndroidClz.getIntentProfile(v0));\n        var executor = this.hashCode();\n        var beatText = 'public void android.content.ContextWrapper.startActivity(android.content.Intent)';\n        var beat = newMethodBeat(beatText, executor);\n        android_content_ContextWrapper_clz_method_startActivity_auep.call(this, v0);\n        printBeat(beat);\n    };\n    var android_content_ContextWrapper_clz_method_startActivityAsUser_adh6 = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.UserHandle');\n    android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.implementation = function(v0, v1) {\n        log(\"Intent>>>>>>>\"+radarAndroidClz.getIntentProfile(v0));\n        var executor = this.hashCode();\n        var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.UserHandle)';\n        var beat = newMethodBeat(beatText, executor);\n        android_content_ContextWrapper_clz_method_startActivityAsUser_adh6.call(this, v0, v1);\n        printBeat(beat);\n    };\n    var android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk = android_content_ContextWrapper_clz.startActivityAsUser.overload('android.content.Intent', 'android.os.Bundle', 'android.os.UserHandle');\n    android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.implementation = function(v0, v1, v2) {\n        log(\"Intent>>>>>>>\"+radarAndroidClz.getIntentProfile(v0));\n        log(\"Bundle>>>>>>>\"+radarAndroidClz.getBundleProfile(v1));\n        var executor = this.hashCode();\n        var beatText = 'public void android.content.ContextWrapper.startActivityAsUser(android.content.Intent,android.os.Bundle,android.os.UserHandle)';\n        var beat = newMethodBeat(beatText, executor);\n        android_content_ContextWrapper_clz_method_startActivityAsUser_ilkk.call(this, v0, v1, v2);\n        printBeat(beat);\n    };\n    var android_app_Activity_clz = Java.use('android.app.Activity');\n    var android_app_Activity_clz_method_startActivityForResult_6mkb = android_app_Activity_clz.startActivityForResult.overload('android.content.Intent', 'int', 'android.os.Bundle');\n    android_app_Activity_clz_method_startActivityForResult_6mkb.implementation = function(v0, v1, v2) {\n        log(\"Intent>>>>>>>\"+radarAndroidClz.getIntentProfile(v0));\n        log(\"Flags>>>>>>>\"+v1);\n        log(\"Bundle>>>>>>>\"+radarAndroidClz.getBundleProfile(v2));\n        var executor = this.hashCode();\n        var beatText = 'public void android.app.Activity.startActivityForResult(android.content.Intent,int,android.os.Bundle)';\n        var beat = newMethodBeat(beatText, executor);\n        android_app_Activity_clz_method_startActivityForResult_6mkb.call(this, v0, v1, v2);\n        printBeat(beat);\n    };\n});"
  },
  {
    "path": "js/android_ui.js",
    "content": "function loadDexfile(dexfile) {\n    Java.perform(function() {\n        Java.openClassFile(dexfile).load();\n    });\n};\n\nfunction checkLoadDex(className, dexfile) {\n    Java.perform(function() {\n        if (!classExists(className)) {\n            Java.openClassFile(dexfile).load();\n            //console.log(\"load \" + dexfile);\n        }\n    });\n};\nloadDexfile('/data/local/tmp/radar.dex');\nfunction classExists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nfunction getClassName(obj) {\n    if (obj.getClass) {\n        return obj.getClass().getName();\n    }\n    var javaObject = Java.use(\"java.lang.Object\");\n    return Java.cast(obj, javaObject).getClass().getName();\n}\n\n//str1是否包含str2，str2可用正则表示\nfunction contains(str1, str2) {\n    var reg = RegExp(eval(\"/\" + str2 + \"/\"));\n    if (str1 && str1.match && str1.match(reg)) {\n        return true;\n    } else {\n        return false;\n    }\n};\n\n//创建ArrayList对象用这个方法就好了\nfunction newArrayList() {\n    var ArrayListClz = Java.use('java.util.ArrayList');\n    return ArrayListClz.$new();\n}\n\n//创建HashSet对象用这个方法就好了\nfunction newHashSet() {\n    var HashSetClz = Java.use('java.util.HashSet');\n    return HashSetClz.$new();\n}\n\n//创建HashMap对象用这个方法就好了\nfunction newHashMap() {\n    var HashMapClz = Java.use('java.util.HashMap');\n    return HashMapClz.$new();\n}\n\nfunction log(str) {\n    console.log(str);\n};\n\n//虽然我们习惯用fastjson一行将对象转成json字符串，但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来，很多人不知道。在frida中所有代码都是透明的，你随便调......\nfunction toJson(javaObject) {\n    var gsonClz = Java.use(\"com.google.gson.Gson\");\n    var toJsonMethod = gsonClz.toJson.overload(\"java.lang.Object\");\n    return toJsonMethod.call(gsonClz.$new(), javaObject);\n};\n\nfunction getBaseContext() {\n    var currentApplication = Java.use('android.app.ActivityThread').currentApplication();\n    var context = currentApplication.getApplicationContext();\n    return context; //Java.scheduleOnMainThread(fn):\n};\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while (new Date().getTime() < startTime) {}\n};\n\nfunction fastTojson(javaObject) {\n    var JSONClz = Java.use(\"gz.com.alibaba.fastjson.JSON\");\n    return JSONClz.toJSONString(javaObject);\n};\n\nfunction findViewById(viewId) {\n    var report = \"\";\n    Java.perform(function() {\n        var radarAndroidClz = Java.use(\"gz.radar.Android\");\n        var viewInfo = radarAndroidClz.getViewInfo(viewId + \"\");\n        if (!viewInfo) {\n            report += \"Not Found View.\"\n            return;\n        }\n        report += (\"------------------View--------------------\") + \"\\n\";\n        report += (\"View Id: \" + viewInfo.getViewId()) + \"\\n\";\n        report += (\"View IdName: \" + viewInfo.getViewIdName()) + \"\\n\";\n        report += (\"View Class: \" + viewInfo.getName()) + \"\\n\";\n        report += (\"View SuperClass: \" + viewInfo.getSuperClazz()) + \"\\n\";\n        report += (\"View ImplementInterfaces: \" + viewInfo.getImplementInterfaces()) + \"\\n\";\n        var androidApkFields = viewInfo.getAndroidApkFields();\n        report += (\"View Fields: \" + androidApkFields.length) + \"\\n\";\n        for (var j = 0; j < androidApkFields.length; j++) {\n            report += (\"\\t\" + androidApkFields[j].toLine()) + \"\\n\";\n        }\n        var methods = viewInfo.methods();\n        report += (\"View Methods: \" + methods.length) + \"\\n\";\n        for (var j = 0; j < methods.length; j++) {\n            report += (\"\\t\" + methods[j]) + \"\\n\";\n        }\n    });\n    log(report);\n}\n\nfunction startActivity(activityName) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.startActivity(activityName);\n    });\n}\n\nfunction contextStartActivity(activityName) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.contextStartActivity(activityName);\n    });\n}\n\nfunction contextStartActivityForNewTask(activityName) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.contextStartActivityForNewTask(activityName);\n    });\n}\n\nfunction topActivityStartActivity(activityName) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.topActivityStartActivity(activityName);\n    });\n}\n\nfunction home() {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.home();\n    });\n}\n\nfunction back() {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.back();\n    });\n}\n\nfunction finishCurrentActivity() {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        androidUIClz.finishCurrentActivity();\n    });\n}\n\nfunction clickByText(text) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        log(androidUIClz.clickByText(text));\n    });\n}\n\nfunction clickById(id) {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        log(androidUIClz.clickById(id));\n    });\n}\n\nfunction hover(x,y,upStepLength) {\n    Java.perform(function() {\n        var androidui = Java.use(\"gz.radar.AndroidUI\");\n        androidui.hover(x,y,upStepLength);\n    });\n}\n\nfunction viewTree() {\n    Java.perform(function() {\n        var androidUIClz = Java.use(\"gz.radar.AndroidUI\");\n        log(androidUIClz.viewTree());\n    });\n}"
  },
  {
    "path": "js/apk_shell_scanner.js",
    "content": "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.');});"
  },
  {
    "path": "js/bypass_frida_svc_detect.js",
    "content": "# 自实现系统函数一个重要的前提就是它们都有标准的系统调用号，标准的机器码。所以我们绕过的时候也可以用同样的思路。\n# Frida的Memory API可以直接查找整个系统的内存内容，我们直接搜索对应函数的特征码，定位到之后再使用Interceptor进行Hook。（要注意每个架构对应的特征可能不一样）\n\nfunction hookSysOpen() {\n    let SYS_OPEN;\n    let SVC_INSTRUCTION_HEX;\n    const arch = Process.arch;\n\n    if (arch === \"arm64\") {\n        SYS_OPEN = 56;  // ARM64架构下open系统调用的编号\n        SVC_INSTRUCTION_HEX = \"01 00 00 D4\";  // ARM64架构下svc指令的十六进制表示\n    } else if (arch === \"arm\") {\n        SYS_OPEN = 5;  // ARM架构下open系统调用的编号\n        SVC_INSTRUCTION_HEX = \"00 00 00 EF\";  // ARM架构下svc指令的十六进制表示\n    } else {\n        console.log(\"不支持的架构: \" + arch);\n        return;\n    }\n\n    console.log(\"当前架构: \" + arch);\n    console.log(\"开始搜索SYS_OPEN系统调用...\");\n\n    //系统调用指令（如svc）通常位于可执行代码段中（r-x）\n    Process.enumerateRanges('r-x').forEach(function(range) {\n        if (range.file && range.file.path && range.file.path.endsWith(\".so\")) {\n            console.log(\"搜索模块: \" + range.file.path);\n            \n            Memory.scan(range.base, range.size, SVC_INSTRUCTION_HEX, {\n                onMatch: function(address) {\n                    let sysCallNumber;\n                    if (arch === \"arm64\") {\n                        // 在ARM64中，系统调用号在svc指令之前的指令中\n                        sysCallNumber = address.sub(4).readU32() & 0xFFFF;\n                    } else if (arch === \"arm\") {\n                        // 在ARM中，系统调用号通常在r7寄存器中，这里我们只能近似处理\n                        sysCallNumber = address.sub(4).readU16() & 0xFF;\n                    }\n                    \n                    if (sysCallNumber === SYS_OPEN) {\n                        console.log(\"找到SYS_OPEN调用，地址: \" + address);\n                        \n                        Interceptor.attach(address, {\n                            onEnter: function(args) {\n                                let fileName;\n                                if (arch === \"arm64\") {\n                                    fileName = args[1].readUtf8String();\n                                } else if (arch === \"arm\") {\n                                    fileName = args[0].readUtf8String();\n                                }\n                                console.log(\"SYS_OPEN被调用，文件名: \" + fileName);\n                            },\n                            onLeave: function(retval) {\n                                console.log(\"SYS_OPEN返回值: \" + retval);\n                            }\n                        });\n                    }\n                },\n                onComplete: function() {\n                    console.log(\"搜索完成\");\n                }\n            });\n        }\n    });\n}\n\nhookSysOpen();\n"
  },
  {
    "path": "js/bypass_root_detect.js",
    "content": "/*\nOriginal author: Daniele Linguaglossa\n28/07/2021 -    Edited by Simone Quatrini\n                Code amended to correctly run on the latest frida version\n        \t\tAdded controls to exclude Magisk Manager\n*/\n\nJava.perform(function() {\n    var RootPackages = [\"com.noshufou.android.su\", \"com.noshufou.android.su.elite\", \"eu.chainfire.supersu\",\n        \"com.koushikdutta.superuser\", \"com.thirdparty.superuser\", \"com.yellowes.su\", \"com.koushikdutta.rommanager\",\n        \"com.koushikdutta.rommanager.license\", \"com.dimonvideo.luckypatcher\", \"com.chelpus.lackypatch\",\n        \"com.ramdroid.appquarantine\", \"com.ramdroid.appquarantinepro\", \"com.devadvance.rootcloak\", \"com.devadvance.rootcloakplus\",\n        \"de.robv.android.xposed.installer\", \"com.saurik.substrate\", \"com.zachspong.temprootremovejb\", \"com.amphoras.hidemyroot\",\n        \"com.amphoras.hidemyrootadfree\", \"com.formyhm.hiderootPremium\", \"com.formyhm.hideroot\", \"me.phh.superuser\",\n        \"eu.chainfire.supersu.pro\", \"com.kingouser.com\", \"com.topjohnwu.magisk\"\n    ];\n\n    var RootBinaries = [\"su\", \"busybox\", \"supersu\", \"Superuser.apk\", \"KingoUser.apk\", \"SuperSu.apk\", \"magisk\"];\n\n    var RootProperties = {\n        \"ro.build.selinux\": \"1\",\n        \"ro.debuggable\": \"0\",\n        \"service.adb.root\": \"0\",\n        \"ro.secure\": \"1\"\n    };\n\n    var RootPropertiesKeys = [];\n\n    for (var k in RootProperties) RootPropertiesKeys.push(k);\n\n    var PackageManager = Java.use(\"android.app.ApplicationPackageManager\");\n\n    var Runtime = Java.use('java.lang.Runtime');\n\n    var NativeFile = Java.use('java.io.File');\n\n    var String = Java.use('java.lang.String');\n\n    var SystemProperties = Java.use('android.os.SystemProperties');\n\n    var BufferedReader = Java.use('java.io.BufferedReader');\n\n    var ProcessBuilder = Java.use('java.lang.ProcessBuilder');\n\n    var StringBuffer = Java.use('java.lang.StringBuffer');\n\n    var loaded_classes = Java.enumerateLoadedClassesSync();\n\n    send(\"Loaded \" + loaded_classes.length + \" classes!\");\n\n    var useKeyInfo = false;\n\n    var useProcessManager = false;\n\n    send(\"loaded: \" + loaded_classes.indexOf('java.lang.ProcessManager'));\n\n    if (loaded_classes.indexOf('java.lang.ProcessManager') != -1) {\n        try {\n            //useProcessManager = true;\n            //var ProcessManager = Java.use('java.lang.ProcessManager');\n        } catch (err) {\n            send(\"ProcessManager Hook failed: \" + err);\n        }\n    } else {\n        send(\"ProcessManager hook not loaded\");\n    }\n\n    var KeyInfo = null;\n\n    if (loaded_classes.indexOf('android.security.keystore.KeyInfo') != -1) {\n        try {\n            //useKeyInfo = true;\n            //var KeyInfo = Java.use('android.security.keystore.KeyInfo');\n        } catch (err) {\n            send(\"KeyInfo Hook failed: \" + err);\n        }\n    } else {\n        send(\"KeyInfo hook not loaded\");\n    }\n\n    PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pname, flags) {\n        var shouldFakePackage = (RootPackages.indexOf(pname) > -1);\n        if (shouldFakePackage) {\n            send(\"Bypass root check for package: \" + pname);\n            pname = \"set.package.name.to.a.fake.one.so.we.can.bypass.it\";\n        }\n        return this.getPackageInfo.overload('java.lang.String', 'int').call(this, pname, flags);\n    };\n\n    NativeFile.exists.implementation = function() {\n        var name = NativeFile.getName.call(this);\n        var shouldFakeReturn = (RootBinaries.indexOf(name) > -1);\n        if (shouldFakeReturn) {\n            send(\"Bypass return value for binary: \" + name);\n            return false;\n        } else {\n            return this.exists.call(this);\n        }\n    };\n\n    var exec = Runtime.exec.overload('[Ljava.lang.String;');\n    var exec1 = Runtime.exec.overload('java.lang.String');\n    var exec2 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;');\n    var exec3 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;');\n    var exec4 = Runtime.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File');\n    var exec5 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;', 'java.io.File');\n\n    exec5.implementation = function(cmd, env, dir) {\n        if (cmd.indexOf(\"getprop\") != -1 || cmd == \"mount\" || cmd.indexOf(\"build.prop\") != -1 || cmd == \"id\" || cmd == \"sh\") {\n            var fakeCmd = \"grep\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        if (cmd == \"su\") {\n            var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        return exec5.call(this, cmd, env, dir);\n    };\n\n    exec4.implementation = function(cmdarr, env, file) {\n        for (var i = 0; i < cmdarr.length; i = i + 1) {\n            var tmp_cmd = cmdarr[i];\n            if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd == \"mount\" || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd == \"id\" || tmp_cmd == \"sh\") {\n                var fakeCmd = \"grep\";\n                send(\"Bypass \" + cmdarr + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n\n            if (tmp_cmd == \"su\") {\n                var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n                send(\"Bypass \" + cmdarr + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n        }\n        return exec4.call(this, cmdarr, env, file);\n    };\n\n    exec3.implementation = function(cmdarr, envp) {\n        for (var i = 0; i < cmdarr.length; i = i + 1) {\n            var tmp_cmd = cmdarr[i];\n            if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd == \"mount\" || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd == \"id\" || tmp_cmd == \"sh\") {\n                var fakeCmd = \"grep\";\n                send(\"Bypass \" + cmdarr + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n\n            if (tmp_cmd == \"su\") {\n                var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n                send(\"Bypass \" + cmdarr + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n        }\n        return exec3.call(this, cmdarr, envp);\n    };\n\n    exec2.implementation = function(cmd, env) {\n        if (cmd.indexOf(\"getprop\") != -1 || cmd == \"mount\" || cmd.indexOf(\"build.prop\") != -1 || cmd == \"id\" || cmd == \"sh\") {\n            var fakeCmd = \"grep\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        if (cmd == \"su\") {\n            var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        return exec2.call(this, cmd, env);\n    };\n\n    exec.implementation = function(cmd) {\n        for (var i = 0; i < cmd.length; i = i + 1) {\n            var tmp_cmd = cmd[i];\n            if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd == \"mount\" || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd == \"id\" || tmp_cmd == \"sh\") {\n                var fakeCmd = \"grep\";\n                send(\"Bypass \" + cmd + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n\n            if (tmp_cmd == \"su\") {\n                var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n                send(\"Bypass \" + cmd + \" command\");\n                return exec1.call(this, fakeCmd);\n            }\n        }\n\n        return exec.call(this, cmd);\n    };\n\n    exec1.implementation = function(cmd) {\n        if (cmd.indexOf(\"getprop\") != -1 || cmd == \"mount\" || cmd.indexOf(\"build.prop\") != -1 || cmd == \"id\" || cmd == \"sh\") {\n            var fakeCmd = \"grep\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        if (cmd == \"su\") {\n            var fakeCmd = \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\";\n            send(\"Bypass \" + cmd + \" command\");\n            return exec1.call(this, fakeCmd);\n        }\n        return exec1.call(this, cmd);\n    };\n\n    String.contains.implementation = function(name) {\n        if (name == \"test-keys\") {\n            send(\"Bypass test-keys check\");\n            return false;\n        }\n        return this.contains.call(this, name);\n    };\n\n    var get = SystemProperties.get.overload('java.lang.String');\n\n    get.implementation = function(name) {\n        if (RootPropertiesKeys.indexOf(name) != -1) {\n            send(\"Bypass \" + name);\n            return RootProperties[name];\n        }\n        return this.get.call(this, name);\n    };\n\n    Interceptor.attach(Module.findExportByName(\"libc.so\", \"fopen\"), {\n        onEnter: function(args) {\n            var path = Memory.readCString(args[0]);\n            path = path.split(\"/\");\n            var executable = path[path.length - 1];\n            var shouldFakeReturn = (RootBinaries.indexOf(executable) > -1)\n            if (shouldFakeReturn) {\n                Memory.writeUtf8String(args[0], \"/notexists\");\n                send(\"Bypass native fopen\");\n            }\n        },\n        onLeave: function(retval) {\n\n        }\n    });\n\n    Interceptor.attach(Module.findExportByName(\"libc.so\", \"system\"), {\n        onEnter: function(args) {\n            var cmd = Memory.readCString(args[0]);\n            send(\"SYSTEM CMD: \" + cmd);\n            if (cmd.indexOf(\"getprop\") != -1 || cmd == \"mount\" || cmd.indexOf(\"build.prop\") != -1 || cmd == \"id\") {\n                send(\"Bypass native system: \" + cmd);\n                Memory.writeUtf8String(args[0], \"grep\");\n            }\n            if (cmd == \"su\") {\n                send(\"Bypass native system: \" + cmd);\n                Memory.writeUtf8String(args[0], \"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\");\n            }\n        },\n        onLeave: function(retval) {\n\n        }\n    });\n\n    /*\n\n    TO IMPLEMENT:\n\n    Exec Family\n\n    int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0);\n    int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);\n    int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0);\n    int execlpe(const char *file, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);\n    int execv(const char *path, char *const argv[]);\n    int execve(const char *path, char *const argv[], char *const envp[]);\n    int execvp(const char *file, char *const argv[]);\n    int execvpe(const char *file, char *const argv[], char *const envp[]);\n\n    */\n\n\n    BufferedReader.readLine.overload('boolean').implementation = function() {\n        var text = this.readLine.overload('boolean').call(this);\n        if (text === null) {\n            // just pass , i know it's ugly as hell but test != null won't work :(\n        } else {\n            var shouldFakeRead = (text.indexOf(\"ro.build.tags=test-keys\") > -1);\n            if (shouldFakeRead) {\n                send(\"Bypass build.prop file read\");\n                text = text.replace(\"ro.build.tags=test-keys\", \"ro.build.tags=release-keys\");\n            }\n        }\n        return text;\n    };\n\n    var executeCommand = ProcessBuilder.command.overload('java.util.List');\n\n    ProcessBuilder.start.implementation = function() {\n        var cmd = this.command.call(this);\n        var shouldModifyCommand = false;\n        for (var i = 0; i < cmd.size(); i = i + 1) {\n            var tmp_cmd = cmd.get(i).toString();\n            if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd.indexOf(\"mount\") != -1 || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd.indexOf(\"id\") != -1) {\n                shouldModifyCommand = true;\n            }\n        }\n        if (shouldModifyCommand) {\n            send(\"Bypass ProcessBuilder \" + cmd);\n            this.command.call(this, [\"grep\"]);\n            return this.start.call(this);\n        }\n        if (cmd.indexOf(\"su\") != -1) {\n            send(\"Bypass ProcessBuilder \" + cmd);\n            this.command.call(this, [\"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\"]);\n            return this.start.call(this);\n        }\n\n        return this.start.call(this);\n    };\n\n    if (useProcessManager) {\n        var ProcManExec = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.io.File', 'boolean');\n        var ProcManExecVariant = ProcessManager.exec.overload('[Ljava.lang.String;', '[Ljava.lang.String;', 'java.lang.String', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'java.io.FileDescriptor', 'boolean');\n\n        ProcManExec.implementation = function(cmd, env, workdir, redirectstderr) {\n            var fake_cmd = cmd;\n            for (var i = 0; i < cmd.length; i = i + 1) {\n                var tmp_cmd = cmd[i];\n                if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd == \"mount\" || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd == \"id\") {\n                    var fake_cmd = [\"grep\"];\n                    send(\"Bypass \" + cmdarr + \" command\");\n                }\n\n                if (tmp_cmd == \"su\") {\n                    var fake_cmd = [\"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\"];\n                    send(\"Bypass \" + cmdarr + \" command\");\n                }\n            }\n            return ProcManExec.call(this, fake_cmd, env, workdir, redirectstderr);\n        };\n\n        ProcManExecVariant.implementation = function(cmd, env, directory, stdin, stdout, stderr, redirect) {\n            var fake_cmd = cmd;\n            for (var i = 0; i < cmd.length; i = i + 1) {\n                var tmp_cmd = cmd[i];\n                if (tmp_cmd.indexOf(\"getprop\") != -1 || tmp_cmd == \"mount\" || tmp_cmd.indexOf(\"build.prop\") != -1 || tmp_cmd == \"id\") {\n                    var fake_cmd = [\"grep\"];\n                    send(\"Bypass \" + cmdarr + \" command\");\n                }\n\n                if (tmp_cmd == \"su\") {\n                    var fake_cmd = [\"justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled\"];\n                    send(\"Bypass \" + cmdarr + \" command\");\n                }\n            }\n            return ProcManExecVariant.call(this, fake_cmd, env, directory, stdin, stdout, stderr, redirect);\n        };\n    }\n\n    if (useKeyInfo) {\n        KeyInfo.isInsideSecureHardware.implementation = function() {\n            send(\"Bypass isInsideSecureHardware\");\n            return true;\n        }\n    }\n\n});\n"
  },
  {
    "path": "js/bypass_vpn_detect.js",
    "content": "function bypassVPNDetect(){\n    Java.perform(function(){\n        var NetworkInterface = Java.use(\"java.net.NetworkInterface\")\n        NetworkInterface.getAll.implementation = function(){\n            var nis = this.getAll()\n            console.log(\"call getAll function !!!\")\n            nis.forEach(function(ni){\n                if (ni.name.value.indexOf(\"tun0\")>=0 || ni.name.value.indexOf(\"ppp0\")>=0 ||\n                ni.displayName.value.indexOf(\"tun0\")>=0 || ni.displayName.value.indexOf(\"ppp0\")>=0){\n                    ni.name.value = \"xxxx\"\n                    ni.displayName.value = \"xxxx\"\n                }\n            })\n            return nis\n        }\n\n        var can_hook = false\n        var ConnectivityManager = Java.use(\"android.net.ConnectivityManager\");\n        ConnectivityManager.getNetworkInfo.overload('int').implementation = function(){\n            if(arguments[0] == 17){\n                can_hook = true\n            }\n            var ret = this.getNetworkInfo(arguments[0])\n            return ret\n        }\n\n        var NetworkInfo = Java.use(\"android.net.NetworkInfo\")\n        NetworkInfo.isConnected.implementation = function(){\n            let ret = this.isConnected()\n            if(can_hook){\n                ret = false\n                can_hook = false\n                console.log(\"call isConnected function !!!\")\n            }\n            return ret\n        }\n\n        var NetworkCapabilities = Java.use(\"android.net.NetworkCapabilities\")\n        NetworkCapabilities.hasTransport.implementation = function(){\n            var ret = this.hasTransport(arguments[0])\n            if(arguments[0] == 4){\n                console.log(\"call hasTransport function !!!\")\n                ret = false\n            }\n            return ret\n        }\n\n        NetworkCapabilities.transportNameOf.overload('int').implementation = function(){\n            var ret = this.transportNameOf(arguments[0])\n            if(ret.indexOf(\"VPN\") >= 0){\n                ret = \"WIFI\"\n            }\n            return ret;\n        }\n    })\n}\n\nsetImmediate(bypassVPNDetect)"
  },
  {
    "path": "js/cipher.js",
    "content": "//javax.crypto.Cipher:?\nfunction classExists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nfunction methodInBeat(invokeId, timestamp, methodName, executor) {\n\tvar startTime = timestamp;\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var currentThread = threadClz.currentThread();\n    var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new());\n    var str = (\"------------startFlag:\" + invokeId + \",objectHash:\"+executor+\",thread(id:\" + currentThread.getId() +\",name:\" + currentThread.getName() + \"),timestamp:\" + startTime+\"---------------\\n\");\n    str += methodName + \"\\n\";\n    str += stackInfo.substring(20);\n    str += (\"------------endFlag:\" + invokeId + \",usedtime:\" + (new Date().getTime() - startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nfunction log(str) {\n    console.log(str);\n};\n\nJava.perform(function() {\n    var java_security_SecureRandom_clz = Java.use('java.security.SecureRandom');\n    var java_security_SecureRandom_clz_method_setSeed_tsea = java_security_SecureRandom_clz.setSeed.overload('long');\n    java_security_SecureRandom_clz_method_setSeed_tsea.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        java_security_SecureRandom_clz_method_setSeed_tsea.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public void java.security.SecureRandom.setSeed(long)', executor);\n    };\n    var java_security_SecureRandom_clz_method_setSeed_c9w7 = java_security_SecureRandom_clz.setSeed.overload('[B');\n    java_security_SecureRandom_clz_method_setSeed_c9w7.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        java_security_SecureRandom_clz_method_setSeed_c9w7.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public synchronized void java.security.SecureRandom.setSeed(byte[])', executor);\n    };\n    var javax_crypto_Cipher_clz = Java.use('javax.crypto.Cipher');\n    var javax_crypto_Cipher_clz_method_doFinal_std2 = javax_crypto_Cipher_clz.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer');\n    javax_crypto_Cipher_clz_method_doFinal_std2.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_std2.call(this, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_chy9 = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec');\n    javax_crypto_Cipher_clz_method_init_chy9.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_chy9.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n    };\n    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');\n    javax_crypto_Cipher_clz_method_tryTransform_s2x4.implementation = function(v0, v1, v2, v3, v4) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_tryTransform_s2x4.call(javax_crypto_Cipher_clz, v0, v1, v2, v3, v4);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_da7l = javax_crypto_Cipher_clz.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom');\n    javax_crypto_Cipher_clz_method_init_da7l.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_da7l.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.cert.Certificate,java.security.SecureRandom) throws java.security.InvalidKeyException', executor);\n    };\n    var javax_crypto_Cipher_clz_method_update_ok86 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int');\n    javax_crypto_Cipher_clz_method_update_ok86.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_update_ok86.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[],int,int)', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot = javax_crypto_Cipher_clz.getExemptionMechanism.overload();\n    javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getExemptionMechanism_lyot.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final javax.crypto.ExemptionMechanism javax.crypto.Cipher.getExemptionMechanism()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_tryCombinations_cyp9 = javax_crypto_Cipher_clz.tryCombinations.overload('java.security.Key', 'java.security.Provider', '[Ljava.lang.String;');\n    javax_crypto_Cipher_clz_method_tryCombinations_cyp9.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_tryCombinations_cyp9.call(javax_crypto_Cipher_clz, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_uuhl = javax_crypto_Cipher_clz.init.overload('int', 'java.security.cert.Certificate');\n    javax_crypto_Cipher_clz_method_init_uuhl.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_uuhl.call(this, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.cert.Certificate) throws java.security.InvalidKeyException', executor);\n    };\n    var javax_crypto_Cipher_clz_method_getInstance_ot5d = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String', 'java.lang.String');\n    javax_crypto_Cipher_clz_method_getInstance_ot5d.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getInstance_ot5d.call(javax_crypto_Cipher_clz, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getOutputSize_f23n = javax_crypto_Cipher_clz.getOutputSize.overload('int');\n    javax_crypto_Cipher_clz_method_getOutputSize_f23n.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getOutputSize_f23n.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getOutputSize(int)', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_update_tx68 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int', '[B', 'int');\n    javax_crypto_Cipher_clz_method_update_tx68.implementation = function(v0, v1, v2, v3, v4) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_update_tx68.call(this, v0, v1, v2, v3, v4);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[],int) throws javax.crypto.ShortBufferException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_ihw9 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int', '[B', 'int');\n    javax_crypto_Cipher_clz_method_doFinal_ihw9.implementation = function(v0, v1, v2, v3, v4) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_ihw9.call(this, v0, v1, v2, v3, v4);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_34ei = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom');\n    javax_crypto_Cipher_clz_method_init_34ei.implementation = function(v0, v1, v2, v3) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_34ei.call(this, v0, v1, v2, v3);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n    };\n    var javax_crypto_Cipher_clz_method_getAlgorithm_lyxb = javax_crypto_Cipher_clz.getAlgorithm.overload();\n    javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getAlgorithm_lyxb.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final java.lang.String javax.crypto.Cipher.getAlgorithm()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_invalidTransformation_ntcg = javax_crypto_Cipher_clz.invalidTransformation.overload('java.lang.String');\n    javax_crypto_Cipher_clz_method_invalidTransformation_ntcg.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_invalidTransformation_ntcg.call(javax_crypto_Cipher_clz, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'private static java.security.NoSuchAlgorithmException javax.crypto.Cipher.invalidTransformation(java.lang.String) throws java.security.NoSuchAlgorithmException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getInstance_oj4j = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String', 'java.security.Provider');\n    javax_crypto_Cipher_clz_method_getInstance_oj4j.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getInstance_oj4j.call(javax_crypto_Cipher_clz, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getCipher_2lau = javax_crypto_Cipher_clz.getCipher.overload('java.lang.String', 'java.security.Provider');\n    javax_crypto_Cipher_clz_method_getCipher_2lau.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getCipher_2lau.call(javax_crypto_Cipher_clz, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed = javax_crypto_Cipher_clz.checkInputOffsetAndCount.overload('int', 'int', 'int');\n    javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_checkInputOffsetAndCount_q4ed.call(javax_crypto_Cipher_clz, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'private static void javax.crypto.Cipher.checkInputOffsetAndCount(int,int,int)', executor);\n    };\n    var javax_crypto_Cipher_clz_method_update_mmvk = javax_crypto_Cipher_clz.update.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer');\n    javax_crypto_Cipher_clz_method_update_mmvk.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_update_mmvk.call(this, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(java.nio.ByteBuffer,java.nio.ByteBuffer) throws javax.crypto.ShortBufferException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getProvider_u7pr = javax_crypto_Cipher_clz.getProvider.overload();\n    javax_crypto_Cipher_clz_method_getProvider_u7pr.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getProvider_u7pr.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final java.security.Provider javax.crypto.Cipher.getProvider()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_updateAAD_alep = javax_crypto_Cipher_clz.updateAAD.overload('[B', 'int', 'int');\n    javax_crypto_Cipher_clz_method_updateAAD_alep.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_updateAAD_alep.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[],int,int)', executor);\n    };\n    var javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh = javax_crypto_Cipher_clz.getMaxAllowedKeyLength.overload('java.lang.String');\n    javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getMaxAllowedKeyLength_4cvh.call(javax_crypto_Cipher_clz, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'public static final int javax.crypto.Cipher.getMaxAllowedKeyLength(java.lang.String) throws java.security.NoSuchAlgorithmException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_update_bto4 = javax_crypto_Cipher_clz.update.overload('[B');\n    javax_crypto_Cipher_clz_method_update_bto4.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_update_bto4.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.update(byte[])', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_dxgo = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int', '[B');\n    javax_crypto_Cipher_clz_method_doFinal_dxgo.implementation = function(v0, v1, v2, v3) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_dxgo.call(this, v0, v1, v2, v3);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getInstance_03ug = javax_crypto_Cipher_clz.getInstance.overload('java.lang.String');\n    javax_crypto_Cipher_clz_method_getInstance_03ug.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getInstance_03ug.call(javax_crypto_Cipher_clz, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'public static final javax.crypto.Cipher javax.crypto.Cipher.getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException,javax.crypto.NoSuchPaddingException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_u19e = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters');\n    javax_crypto_Cipher_clz_method_init_u19e.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_u19e.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n    };\n    var javax_crypto_Cipher_clz_method_updateAAD_ojmm = javax_crypto_Cipher_clz.updateAAD.overload('java.nio.ByteBuffer');\n    javax_crypto_Cipher_clz_method_updateAAD_ojmm.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_updateAAD_ojmm.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(java.nio.ByteBuffer)', executor);\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_ixw8 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int', 'int');\n    javax_crypto_Cipher_clz_method_doFinal_ixw8.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_ixw8.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal(byte[],int,int) throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getBlockSize_wjca = javax_crypto_Cipher_clz.getBlockSize.overload();\n    javax_crypto_Cipher_clz_method_getBlockSize_wjca.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getBlockSize_wjca.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.getBlockSize()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_wrap_05i9 = javax_crypto_Cipher_clz.wrap.overload('java.security.Key');\n    javax_crypto_Cipher_clz_method_wrap_05i9.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_wrap_05i9.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.wrap(java.security.Key) throws javax.crypto.IllegalBlockSizeException,java.security.InvalidKeyException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m = javax_crypto_Cipher_clz.getMaxAllowedParameterSpec.overload('java.lang.String');\n    javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getMaxAllowedParameterSpec_cp3m.call(javax_crypto_Cipher_clz, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'public static final java.security.spec.AlgorithmParameterSpec javax.crypto.Cipher.getMaxAllowedParameterSpec(java.lang.String) throws java.security.NoSuchAlgorithmException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_checkTransformation_qoag = javax_crypto_Cipher_clz.checkTransformation.overload('java.lang.String');\n    javax_crypto_Cipher_clz_method_checkTransformation_qoag.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_checkTransformation_qoag.call(javax_crypto_Cipher_clz, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'private static [Ljava.lang.String; javax.crypto.Cipher.checkTransformation(java.lang.String) throws java.security.NoSuchAlgorithmException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getParameters_6auy = javax_crypto_Cipher_clz.getParameters.overload();\n    javax_crypto_Cipher_clz_method_getParameters_6auy.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getParameters_6auy.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final java.security.AlgorithmParameters javax.crypto.Cipher.getParameters()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getSpi_963r = javax_crypto_Cipher_clz.getSpi.overload('java.security.Key');\n    javax_crypto_Cipher_clz_method_getSpi_963r.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getSpi_963r.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi(java.security.Key)', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_k1n2 = javax_crypto_Cipher_clz.doFinal.overload('[B');\n    javax_crypto_Cipher_clz_method_doFinal_k1n2.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_k1n2.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal(byte[]) throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getSpi_vlc2 = javax_crypto_Cipher_clz.getSpi.overload();\n    javax_crypto_Cipher_clz_method_getSpi_vlc2.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getSpi_vlc2.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'private javax.crypto.CipherSpi javax.crypto.Cipher.getSpi()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_getIV_2b1b = javax_crypto_Cipher_clz.getIV.overload();\n    javax_crypto_Cipher_clz_method_getIV_2b1b.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_getIV_2b1b.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.getIV()', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_unwrap_azqe = javax_crypto_Cipher_clz.unwrap.overload('[B', 'java.lang.String', 'int');\n    javax_crypto_Cipher_clz_method_unwrap_azqe.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_unwrap_azqe.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_l66q = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key');\n    javax_crypto_Cipher_clz_method_init_l66q.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_l66q.call(this, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key) throws java.security.InvalidKeyException', executor);\n    };\n    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');\n    javax_crypto_Cipher_clz_method_init_w1fs.implementation = function(v0, v1, v2, v3) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_w1fs.call(this, v0, v1, v2, v3);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        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);\n    };\n    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');\n    javax_crypto_Cipher_clz_method_tryTransformWithProvider_an3a.implementation = function(v0, v1, v2, v3) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_tryTransformWithProvider_an3a.call(javax_crypto_Cipher_clz, v0, v1, v2, v3);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        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);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_checkMode_8g2b = javax_crypto_Cipher_clz.checkMode.overload('int');\n    javax_crypto_Cipher_clz_method_checkMode_8g2b.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_checkMode_8g2b.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'private void javax.crypto.Cipher.checkMode(int)', executor);\n    };\n    var javax_crypto_Cipher_clz_method_update_ll68 = javax_crypto_Cipher_clz.update.overload('[B', 'int', 'int', '[B');\n    javax_crypto_Cipher_clz_method_update_ll68.implementation = function(v0, v1, v2, v3) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_update_ll68.call(this, v0, v1, v2, v3);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.update(byte[],int,int,byte[]) throws javax.crypto.ShortBufferException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_62jl = javax_crypto_Cipher_clz.doFinal.overload();\n    javax_crypto_Cipher_clz_method_doFinal_62jl.implementation = function() {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_62jl.call(this);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final [B javax.crypto.Cipher.doFinal() throws javax.crypto.IllegalBlockSizeException,javax.crypto.BadPaddingException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_init_c9qo = javax_crypto_Cipher_clz.init.overload('int', 'java.security.Key', 'java.security.SecureRandom');\n    javax_crypto_Cipher_clz_method_init_c9qo.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_init_c9qo.call(this, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.init(int,java.security.Key,java.security.SecureRandom) throws java.security.InvalidKeyException', executor);\n    };\n    var javax_crypto_Cipher_clz_method_doFinal_rso8 = javax_crypto_Cipher_clz.doFinal.overload('[B', 'int');\n    javax_crypto_Cipher_clz_method_doFinal_rso8.implementation = function(v0, v1) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_doFinal_rso8.call(this, v0, v1);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final int javax.crypto.Cipher.doFinal(byte[],int) throws javax.crypto.IllegalBlockSizeException,javax.crypto.ShortBufferException,javax.crypto.BadPaddingException', executor);\n        return ret;\n    };\n    var javax_crypto_Cipher_clz_method_updateAAD_ry91 = javax_crypto_Cipher_clz.updateAAD.overload('[B');\n    javax_crypto_Cipher_clz_method_updateAAD_ry91.implementation = function(v0) {\n        var startTime = new Date().getTime();\n        javax_crypto_Cipher_clz_method_updateAAD_ry91.call(this, v0);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = this.hashCode();\n        methodInBeat(invokeId, startTime, 'public final void javax.crypto.Cipher.updateAAD(byte[])', executor);\n    };\n    var javax_crypto_Cipher_clz_method_matchAttribute_mv6j = javax_crypto_Cipher_clz.matchAttribute.overload('java.security.Provider$Service', 'java.lang.String', 'java.lang.String');\n    javax_crypto_Cipher_clz_method_matchAttribute_mv6j.implementation = function(v0, v1, v2) {\n        var startTime = new Date().getTime();\n        var ret = javax_crypto_Cipher_clz_method_matchAttribute_mv6j.call(javax_crypto_Cipher_clz, v0, v1, v2);\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var executor = 'javax_crypto_Cipher_clz';\n        methodInBeat(invokeId, startTime, 'private static boolean javax.crypto.Cipher.matchAttribute(java.security.Provider$Service,java.lang.String,java.lang.String)', executor);\n        return ret;\n    };\n\n});"
  },
  {
    "path": "js/click.js",
    "content": "function methodInBeat(invokeId, timestamp, methodName, executor) {\n\tvar startTime = timestamp;\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var currentThread = threadClz.currentThread();\n    var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new());\n    var str = (\"------------startFlag:\" + invokeId + \",objectHash:\"+executor+\",thread(id:\" + currentThread.getId() +\",name:\" + currentThread.getName() + \"),timestamp:\" + startTime+\"---------------\\n\");\n    str += methodName + \"\\n\";\n    str += stackInfo.substring(20);\n    str += (\"------------endFlag:\" + invokeId + \",usedtime:\" + (new Date().getTime() - startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while (new Date().getTime() < startTime) {}\n};\n\nfunction makeClass(className) {\n    var classClz = Java.use(\"java.lang.Class\");\n    var forNameFunc = classClz.forName.overload(\"java.lang.String\");\n    return forNameFunc.call(classClz, className);\n};\n\nfunction isClass(obj, superClzName) {\n    var objClz = obj.getClass();\n    var superClz = makeClass(superClzName);\n    return superClz.isAssignableFrom(objClz);\n};\n\nJava.perform(function() {\n    var textViewClz = Java.use(\"android.widget.TextView\");\n    var android_view_View_clz = Java.use('android.view.View');\n    var android_view_View_clz_method_performClick_u6ef = android_view_View_clz.performClick.overload();\n    android_view_View_clz_method_performClick_u6ef.implementation = function() {\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var startTime = new Date().getTime();\n        var executor = 'obj:' + this.hashCode();\n        var ret = android_view_View_clz_method_performClick_u6ef.call(this);\n        var clz = this.getClass().getName();\n        var viewId = this.getId();\n        //console.log(\"ViewText: \" + Java.cast(this, textViewClz).getText());\n        console.log(\"ViewClz: \" + clz);\n        console.log(\"ViewId: \" + viewId);\n        methodInBeat(invokeId, startTime, 'public boolean android.view.View.performClick()', executor);\n        return ret;\n    };\n});"
  },
  {
    "path": "js/dump_dex.js",
    "content": "function get_self_process_name() {\n    var openPtr = Module.getExportByName('libc.so', 'open');\n    var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);\n\n    var readPtr = Module.getExportByName(\"libc.so\", \"read\");\n    var read = new NativeFunction(readPtr, \"int\", [\"int\", \"pointer\", \"int\"]);\n\n    var closePtr = Module.getExportByName('libc.so', 'close');\n    var close = new NativeFunction(closePtr, 'int', ['int']);\n\n    var path = Memory.allocUtf8String(\"/proc/self/cmdline\");\n    var fd = open(path, 0);\n    if (fd != -1) {\n        var buffer = Memory.alloc(0x1000);\n\n        var result = read(fd, buffer, 0x1000);\n        close(fd);\n        result = ptr(buffer).readCString();\n        return result;\n    }\n\n    return \"-1\";\n}\n\n\nfunction mkdir(path) {\n    var mkdirPtr = Module.getExportByName('libc.so', 'mkdir');\n    var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']);\n\n\n\n    var opendirPtr = Module.getExportByName('libc.so', 'opendir');\n    var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']);\n\n    var closedirPtr = Module.getExportByName('libc.so', 'closedir');\n    var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']);\n\n    var cPath = Memory.allocUtf8String(path);\n    var dir = opendir(cPath);\n    if (dir != 0) {\n        closedir(dir);\n        return 0;\n    }\n    mkdir(cPath, 755);\n    chmod(path);\n}\n\nfunction chmod(path) {\n    var chmodPtr = Module.getExportByName('libc.so', 'chmod');\n    var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']);\n    var cPath = Memory.allocUtf8String(path);\n    chmod(cPath, 755);\n}\n\nfunction dump_dex() {\n    var libart = Process.findModuleByName(\"libart.so\");\n    var addr_DefineClass = null;\n    var symbols = libart.enumerateSymbols();\n    for (var index = 0; index < symbols.length; index++) {\n        var symbol = symbols[index];\n        var symbol_name = symbol.name;\n        //这个DefineClass的函数签名是Android9的\n        //_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE\n        if (symbol_name.indexOf(\"ClassLinker\") >= 0 &&\n            symbol_name.indexOf(\"DefineClass\") >= 0 &&\n            symbol_name.indexOf(\"Thread\") >= 0 &&\n            symbol_name.indexOf(\"DexFile\") >= 0) {\n            console.log(symbol_name, symbol.address);\n            addr_DefineClass = symbol.address;\n        }\n    }\n    var dex_maps = {};\n    var dex_count = 1;\n\n    console.log(\"[DefineClass:]\", addr_DefineClass);\n    if (addr_DefineClass) {\n        Interceptor.attach(addr_DefineClass, {\n            onEnter: function(args) {\n                var dex_file = args[5];\n                //ptr(dex_file).add(Process.pointerSize) is \"const uint8_t* const begin_;\"\n                //ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is \"const size_t size_;\"\n                var base = ptr(dex_file).add(Process.pointerSize).readPointer();\n                var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();\n\n                if (dex_maps[base] == undefined) {\n                    dex_maps[base] = size;\n                    var magic = ptr(base).readCString();\n                    if (magic.indexOf(\"dex\") == 0) {\n\n                        var process_name = get_self_process_name();\n                        if (process_name != \"-1\") {\n                            var dex_dir_path = \"/data/data/\" + process_name + \"/files/dump_dex_\" + process_name;\n                            mkdir(dex_dir_path);\n                            var dex_path = dex_dir_path + \"/classes\" + (dex_count == 1 ? \"\" : dex_count) + \".dex\";\n                            console.log(\"[find dex]:\", dex_path);\n                            var fd = new File(dex_path, \"wb\");\n                            if (fd && fd != null) {\n                                dex_count++;\n                                var dex_buffer = ptr(base).readByteArray(size);\n                                fd.write(dex_buffer);\n                                fd.flush();\n                                fd.close();\n                                console.log(\"[dump dex]:\", dex_path);\n\n                            }\n                        }\n                    }\n                }\n            },\n            onLeave: function(retval) {}\n        });\n    }\n}\n\nvar is_hook_libart = false;\n\nfunction hook_dlopen() {\n    Interceptor.attach(Module.findExportByName(null, \"dlopen\"), {\n        onEnter: function(args) {\n            var pathptr = args[0];\n            if (pathptr !== undefined && pathptr != null) {\n                var path = ptr(pathptr).readCString();\n                //console.log(\"dlopen:\", path);\n                if (path.indexOf(\"libart.so\") >= 0) {\n                    this.can_hook_libart = true;\n                    console.log(\"[dlopen:]\", path);\n                }\n            }\n        },\n        onLeave: function(retval) {\n            if (this.can_hook_libart && !is_hook_libart) {\n                dump_dex();\n                is_hook_libart = true;\n            }\n        }\n    })\n\n    Interceptor.attach(Module.findExportByName(null, \"android_dlopen_ext\"), {\n        onEnter: function(args) {\n            var pathptr = args[0];\n            if (pathptr !== undefined && pathptr != null) {\n                var path = ptr(pathptr).readCString();\n                //console.log(\"android_dlopen_ext:\", path);\n                if (path.indexOf(\"libart.so\") >= 0) {\n                    this.can_hook_libart = true;\n                    console.log(\"[android_dlopen_ext:]\", path);\n                }\n            }\n        },\n        onLeave: function(retval) {\n            if (this.can_hook_libart && !is_hook_libart) {\n                dump_dex();\n                is_hook_libart = true;\n            }\n        }\n    });\n}\n\n\nsetImmediate(dump_dex);"
  },
  {
    "path": "js/dump_so.js",
    "content": "function dump_so(so_name) {\n    if (Java.available) {\n        Java.perform(function () {\n            var currentApplication = Java.use(\"android.app.ActivityThread\").currentApplication();\n            var dir = currentApplication.getApplicationContext().getFilesDir().getPath();\n            var libso = Process.getModuleByName(so_name);\n            console.log(\"[name]:\", libso.name);\n            console.log(\"[base]:\", libso.base);\n            console.log(\"[size]:\", ptr(libso.size));\n            console.log(\"[path]:\", libso.path);\n            var file_path = dir + \"/\" + libso.name + \"_\" + libso.base + \"_\" + ptr(libso.size) + \".so\";\n            var file_handle = new File(file_path, \"wb\");\n            if (file_handle && file_handle != null) {\n                Memory.protect(ptr(libso.base), libso.size, 'rwx');\n                var libso_buffer = ptr(libso.base).readByteArray(libso.size);\n                file_handle.write(libso_buffer);\n                file_handle.flush();\n                file_handle.close();\n                console.log(\"[dump]:\", file_path);\n            }\n        });\n    }\n}"
  },
  {
    "path": "js/edit_text.js",
    "content": "function methodInBeat(invokeId, timestamp, methodName, executor) {\n\tvar startTime = timestamp;\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var currentThread = threadClz.currentThread();\n    var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new());\n    var str = (\"------------startFlag:\" + invokeId + \",objectHash:\"+executor+\",thread(id:\" + currentThread.getId() +\",name:\" + currentThread.getName() + \"),timestamp:\" + startTime+\"---------------\\n\");\n    str += methodName + \"\\n\";\n    str += stackInfo.substring(20);\n    str += (\"------------endFlag:\" + invokeId + \",usedtime:\" + (new Date().getTime() - startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nfunction makeClass(className) {\n    var classClz = Java.use(\"java.lang.Class\");\n    var forNameFunc = classClz.forName.overload(\"java.lang.String\");\n    return forNameFunc.call(classClz, className);\n};\n\nfunction isClass(obj, superClzName) {\n    var objClz = obj.getClass();\n    var superClz = makeClass(superClzName);\n    return superClz.isAssignableFrom(objClz);\n};\n\nfunction classExists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nJava.perform(function() {\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var textViewClz = Java.use(\"android.widget.TextView\");\n\tvar charSequenceClz = Java.use(\"java.lang.CharSequence\");\n    if (textViewClz.setText) {\n        var setTextFunc = textViewClz.setText.overload(\"java.lang.CharSequence\");\n        setTextFunc.implementation = function(v0) {\n        \tvar startTime = new Date().getTime();\n            setTextFunc.call(this, v0);\n            if (isClass(this, \"android.widget.EditText\")) {\n                var clz = this.getClass().getName();\n                var viewId = this.getId();\n                console.log(\"EditTextClz: \" + clz);\n                console.log(\"ViewId: \" + viewId);\n                console.log(\"text: \" + v0);\n                var invokeId = Math.random().toString(36).slice( - 8);\n        \t\tvar executor = this.hashCode();\n                methodInBeat(invokeId, startTime, 'android.widget.EditText.setText()', executor);\n            }\n        };\n    }\n    //EditText的getText继承自TextView\n    if (textViewClz.getText) {\n        var getTextFunc = textViewClz.getText.overload();\n        getTextFunc.implementation = function() {\n        \tvar startTime = new Date().getTime();\n            var editable = getTextFunc.call(this);\n            if (isClass(this, \"android.widget.EditText\")) {\n                var clz = this.getClass().getName();\n                var viewId = this.getId();\n                console.log(\"EditTextClz: \" + clz);\n                console.log(\"ViewId: \" + viewId);\n                console.log(\"Text: \" + Java.cast(editable, charSequenceClz));\n                var invokeId = Math.random().toString(36).slice( - 8);\n        \t\tvar executor = this.hashCode();\n        \t\tmethodInBeat(invokeId, startTime, 'android.widget.EditText.getText()', executor);\n            }\n            return editable;\n        };\n    }\n    //AppCompatEditText 有自定义的getText所以单独hook\n    if (classExists(\"androidx.appcompat.widget.AppCompatEditText\")) {\n        var appCompatEditTextClz = Java.use(\"androidx.appcompat.widget.AppCompatEditText\");\n        var appCompatEditTextClzGetTextFunc = appCompatEditTextClz.getText.overload();\n        appCompatEditTextClzGetTextFunc.implementation = function() {\n        \tvar startTime = new Date().getTime();\n            var editable = appCompatEditTextClzGetTextFunc.call(this);\n            var clz = this.getClass().getName();\n            var viewId = this.getId();\n            console.log(\"EditTextClz: \" + clz);\n            console.log(\"ViewId: \" + viewId);\n            console.log(\"Text: \" + Java.cast(editable, charSequenceClz));\n            var invokeId = Math.random().toString(36).slice( - 8);\n        \tvar executor = this.hashCode();\n        \tmethodInBeat(invokeId, startTime, 'androidx.appcompat.widget.AppCompatEditText.getText()', executor);\n            return editable;\n        };\n    }\n});"
  },
  {
    "path": "js/file.js",
    "content": "function writeFileAsBase64Content(filepath, base64) {\n    var StringClz = Java.use('java.lang.String');\n    var Base64Clz = Java.use(\"android.util.Base64\");\n    var ByteArrayInputStreamClz = Java.use(\"java.io.ByteArrayInputStream\");\n    var FileOutputStreamClz = Java.use(\"java.io.FileOutputStream\");\n    var FileClz = Java.use(\"java.io.File\");\n    var FileUtilsClz = Java.use(\"android.os.FileUtils\");\n    var javaBase64String = StringClz.$new(base64);\n    var getBytesMehtod = StringClz.getBytes.overload('java.lang.String');\n    var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8');\n    var decodeMethod = Base64Clz.decode.overload('[B', 'int');\n    var originalBinary = decodeMethod.call(Base64Clz, bytes, 0);\n    var bais = ByteArrayInputStreamClz.$new(originalBinary);\n    var fos = FileOutputStreamClz.$new(FileClz.$new(filepath));\n    var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream');\n    copyMehtod.call(FileUtilsClz, bais, fos);\n};\n\nfunction fileExists(filepath) {\n    var FileClz = Java.use(\"java.io.File\");\n    return FileClz.$new(filepath).exists();\n};\n\nrpc.exports = {\n    write: function(filename, contentAsBase64) {\n        Java.perform(function() {\n            writeFileAsBase64Content(filename, contentAsBase64);\n        });\n    },\n    exists: function(filename) {\n    \tvar ret = false;\n        Java.perform(function() {\n            ret = fileExists(filename);\n        });\n        return ret;\n    },\n};"
  },
  {
    "path": "js/find_anit_frida_so.js",
    "content": "function hook_dlopen(){\n    //Android8.0之后加载so通过android_dlopen_ext函数\n    var android_dlopen_ext = Module.findExportByName(null,\"android_dlopen_ext\");\n    console.log(\"addr_android_dlopen_ext\",android_dlopen_ext);\n    Interceptor.attach(android_dlopen_ext,{\n        onEnter:function(args){\n            var pathptr = args[0];\n            if(pathptr!=null && pathptr != undefined){\n                var path = ptr(pathptr).readCString();\n                console.log(\"android_dlopen_ext:\",path);\n            }\n        },\n        onLeave:function(retvel){\n            console.log(\"leave!\");\n        }\n    })\n}\nhook_dlopen()\n"
  },
  {
    "path": "js/find_boringssl_custom_verify_func.js",
    "content": "var hasAlreadyHooked = false;\n\n//init_proc func start addr\nvar startAddr = null;\n\n//init_proc func end addr\nvar endAddr = null;\n\n// replace your so name eg: xxx.so\nvar somodule = \"libttboringssl.so\";\n\nfunction hook_dlopen(){\n    //Android8.0之后加载so通过android_dlopen_ext函数\n    var android_dlopen_ext = Module.findExportByName(null,\"android_dlopen_ext\");\n    //console.log(\"addr_android_dlopen_ext\",android_dlopen_ext);\n    Interceptor.attach(android_dlopen_ext,{\n        onEnter:function(args){\n            var pathptr = args[0];\n            if(pathptr!=null && pathptr != undefined){\n                var path = ptr(pathptr).readCString();\n                this.path = path;\n                if(path.indexOf(somodule)!=-1){\n                    //console.log(\"android_dlopen_ext:\",path);\n                    hook_custom_verify()\n                }\n            }\n        },\n        onLeave:function(retvel){\n            //console.log(this.path+\" leave!\");\n        }\n    })\n}\n\nconst targetLibrary = somodule;\nconst targetFunction = 'SSL_CTX_set_custom_verify';\nconst functionOffset = 0x47FB0; // 函数偏移量\n\n// 主函数\nfunction hook_SSL_CTX_set_custom_verify() {\n    // 获取模块基址\n    const moduleBase = Module.findBaseAddress(targetLibrary);\n    if (!moduleBase) {\n        console.error(`[!] 无法找到模块: ${targetLibrary}`);\n        return;\n    }\n\n    // 计算绝对地址\n    const targetAddress = moduleBase.add(functionOffset);\n    console.log(`[+] ${targetLibrary} 基址: ${moduleBase}`);\n    console.log(`[+] ${targetFunction} 地址: ${targetAddress}`);\n\n    // 挂钩函数\n    Interceptor.attach(targetAddress, {\n        onEnter: function (args) {\n            console.log(`\\n[+] ${targetFunction} 被调用`);\n            \n            // 打印参数 (根据函数原型可能有不同)\n            // SSL_CTX_set_custom_verify(SSL_CTX *ctx, int mode, SSL_custom_verify_callback callback)\n            console.log(`  - SSL_CTX: ${args[0]}`);\n            console.log(`  - 验证模式: ${args[1]}`);\n            console.log(`  - 回调函数地址: ${args[2]}`);\n            var find_module = Process.findModuleByAddress(args[2]);\n            if (find_module) {\n                console.log(\"回调函数so偏移地址 |--> Module: \" + find_module.name + \" offset:\" + ptr(args[2]).sub(find_module.base));\n            }\n            // 保存参数以便在onLeave中使用\n            this.ctx = args[0];\n            this.mode = args[1].toInt32();\n            this.callback = args[2];\n        },\n        onLeave: function (retval) {\n            console.log(`[+] ${targetFunction} 返回`);\n            console.log(`  - 返回值: ${retval}`);\n            \n            // 如果需要，可以在这里修改返回值\n            // retval.replace(ptr(0x1)); // 示例：强制返回1\n        }\n    });\n}\n\n\nfunction hook_custom_verify() {\n    // 延迟执行以确保模块已加载\n    setTimeout(hook_SSL_CTX_set_custom_verify, 0);\n\n}\nfunction main(){\n    hook_dlopen()\n}\n\nmain()"
  },
  {
    "path": "js/get_device_info.js",
    "content": "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']();}});}"
  },
  {
    "path": "js/hook_artmethod_register.js",
    "content": "function hook_ArtMethodRegister() {\n    var symbols = Module.enumerateSymbolsSync(\"libart.so\");\n\n    var ArtMethodRegisterNative = null;\n    var ArtMethod_PrettyMethod = null;\n    for (var i = 0; i < symbols.length; i++) {\n        var symbol = symbols[i];\n        var address = symbol.address;\n        var name = symbol.name;\n        var indexArtMethod = name.indexOf(\"ArtMethod\");\n        //_ZN3art9ArtMethod14RegisterNativeEPKv android 10\n        if (\n            name.indexOf(\"ArtMethod\") >= 0 &&\n            name.indexOf(\"RegisterNative\") >= 0 &&\n            name.indexOf(\"Callback\") < 0\n        ) {\n            console.log(\"ArtMethod::RegisterNative is at \", address, name);\n            ArtMethodRegisterNative = address;\n        }\n        if (indexArtMethod >= 0 && name.indexOf(\"PrettyMethod\") >= 0 && name.indexOf(\"Eb\") >= 0) {\n            console.log(\"ArtMethod::PrettyMethod is at \", address, name);\n            ArtMethod_PrettyMethod = address;\n        }\n    }\n    var module_libext = null;\n    if (Process.arch === \"arm64\") {\n        module_libext = Module.load(\"/data/data/com.smile.gifmaker/files/libext64.so\");\n    } else if (Process.arch === \"arm\") {\n        module_libext = Module.load(\"/data/data/com.smile.gifmaker/files/libext.so\");\n    }\n    if (module_libext != null) {\n        var addr_PrettyMethod = module_libext.findExportByName(\"PrettyMethod\");\n        var PrettyMethod = new NativeFunction(addr_PrettyMethod, \"void\", [\"pointer\", \"pointer\", \"pointer\", \"int\"]);\n\n        if (ArtMethodRegisterNative) {\n            //var foo_ArtMethod_PrettyMethod = new NativeFunction(ArtMethod_PrettyMethod, \"pointer\", [\"pointer\", \"int\"]);\n            Interceptor.attach(ArtMethodRegisterNative, {\n                onEnter: function (args) {\n                    try {\n                        var result = Memory.alloc(0x100);\n                        var fnPtr_ptr = args[1];\n                        var find_module = Process.findModuleByAddress(fnPtr_ptr);\n                        var offset = ptr(fnPtr_ptr).sub(find_module.base)\n                        PrettyMethod(ArtMethod_PrettyMethod, args[0], result, 0x100);\n                        console.log(\"[ArtMethod_RegisterNative] Method_sig:\", result.readCString(), \"module_name:\", find_module.name, \"offset:\", offset);\n                    } catch (error) {\n                        console.log(error);\n                    }\n\n                }, onLeave: function (retval) {\n\n                }\n            });\n        }\n    }\n}\n\nsetImmediate(hook_ArtMethodRegister)\n\n"
  },
  {
    "path": "js/hook_encryption_algo.js",
    "content": "var N_ENCRYPT_MODE = 1\nvar N_DECRYPT_MODE = 2\nfunction showStacks() {\n    var Exception = Java.use(\"java.lang.Exception\");\n    var ins = Exception.$new(\"Exception\");\n    var straces = ins.getStackTrace();\n    if (undefined == straces || null == straces) {\n        return;\n    }\n    console.log(\"============================= Stack strat=======================\");\n    console.log(\"\");\n    for (var i = 0; i < straces.length; i++) {\n        var str = \"   \" + straces[i].toString();\n        console.log(str);\n    }\n    console.log(\"\");\n    Exception.$dispose();\n}\n//工具相关函数\nvar base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',\n    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));\nfunction stringToBase64(e) {\n    var r, a, c, h, o, t;\n    for (c = e.length, a = 0, r = ''; a < c;) {\n        if (h = 255 & e.charCodeAt(a++), a == c) {\n            r += base64EncodeChars.charAt(h >> 2),\n                r += base64EncodeChars.charAt((3 & h) << 4),\n                r += '==';\n            break\n        }\n        if (o = e.charCodeAt(a++), a == c) {\n            r += base64EncodeChars.charAt(h >> 2),\n                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),\n                r += base64EncodeChars.charAt((15 & o) << 2),\n                r += '=';\n            break\n        }\n        t = e.charCodeAt(a++),\n            r += base64EncodeChars.charAt(h >> 2),\n            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),\n            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),\n            r += base64EncodeChars.charAt(63 & t)\n    }\n    return r\n}\nfunction base64ToString(e) {\n    var r, a, c, h, o, t, d;\n    for (t = e.length, o = 0, d = ''; o < t;) {\n        do\n            r = base64DecodeChars[255 & e.charCodeAt(o++)];\n        while (o < t && r == -1);\n        if (r == -1)\n            break;\n        do\n            a = base64DecodeChars[255 & e.charCodeAt(o++)];\n        while (o < t && a == -1);\n        if (a == -1)\n            break;\n        d += String.fromCharCode(r << 2 | (48 & a) >> 4);\n        do {\n            if (c = 255 & e.charCodeAt(o++), 61 == c)\n                return d;\n            c = base64DecodeChars[c]\n        } while (o < t && c == -1);\n        if (c == -1)\n            break;\n        d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2);\n        do {\n            if (h = 255 & e.charCodeAt(o++), 61 == h)\n                return d;\n            h = base64DecodeChars[h]\n        } while (o < t && h == -1);\n        if (h == -1)\n            break;\n        d += String.fromCharCode((3 & c) << 6 | h)\n    }\n    return d\n}\nfunction hexToBase64(str) {\n    return base64Encode(String.fromCharCode.apply(null, str.replace(/([\\da-fA-F]{2}) ?/g, \"0x$1 \").replace(/ +$/, \"\").split(\" \")));\n}\nfunction base64ToHex(str) {\n    for (var i = 0, bin = base64Decode(str), hex = []; i < bin.length; ++i) {\n        var tmp = bin.charCodeAt(i).toString(16);\n        if (tmp.length === 1)\n            tmp = \"0\" + tmp;\n        hex[hex.length] = tmp;\n    }\n    return hex.join(\"\");\n}\nfunction hexToBytes(str) {\n    var pos = 0;\n    var len = str.length;\n    if (len % 2 != 0) {\n        return null;\n    }\n    len /= 2;\n    var hexA = new Array();\n    for (var i = 0; i < len; i++) {\n        var s = str.substr(pos, 2);\n        var v = parseInt(s, 16);\n        hexA.push(v);\n        pos += 2;\n    }\n    return hexA;\n}\nfunction bytesToHex(arr) {\n    var str = '';\n    var k, j;\n    for (var i = 0; i < arr.length; i++) {\n        k = arr[i];\n        j = k;\n        if (k < 0) {\n            j = k + 256;\n        }\n        if (j < 16) {\n            str += \"0\";\n        }\n        str += j.toString(16);\n    }\n    return str;\n}\nfunction stringToHex(str) {\n    var val = \"\";\n    for (var i = 0; i < str.length; i++) {\n        if (val == \"\")\n            val = str.charCodeAt(i).toString(16);\n        else\n            val += str.charCodeAt(i).toString(16);\n    }\n    return val\n}\nfunction stringToBytes(str) {\n    var ch, st, re = [];\n    for (var i = 0; i < str.length; i++) {\n        ch = str.charCodeAt(i);\n        st = [];\n        do {\n            st.push(ch & 0xFF);\n            ch = ch >> 8;\n        }\n        while (ch);\n        re = re.concat(st.reverse());\n    }\n    return re;\n}\n//将byte[]转成String的方法\nfunction bytesToString(arr) {\n    var str = '';\n    arr = new Uint8Array(arr);\n    for (var i in arr) {\n        str += String.fromCharCode(arr[i]);\n    }\n    return str;\n}\nfunction bytesToBase64(e) {\n    var r, a, c, h, o, t;\n    for (c = e.length, a = 0, r = ''; a < c;) {\n        if (h = 255 & e[a++], a == c) {\n            r += base64EncodeChars.charAt(h >> 2),\n                r += base64EncodeChars.charAt((3 & h) << 4),\n                r += '==';\n            break\n        }\n        if (o = e[a++], a == c) {\n            r += base64EncodeChars.charAt(h >> 2),\n                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),\n                r += base64EncodeChars.charAt((15 & o) << 2),\n                r += '=';\n            break\n        }\n        t = e[a++],\n            r += base64EncodeChars.charAt(h >> 2),\n            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),\n            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),\n            r += base64EncodeChars.charAt(63 & t)\n    }\n    return r\n}\nfunction base64ToBytes(e) {\n    var r, a, c, h, o, t, d;\n    for (t = e.length, o = 0, d = []; o < t;) {\n        do\n            r = base64DecodeChars[255 & e.charCodeAt(o++)];\n        while (o < t && r == -1);\n        if (r == -1)\n            break;\n        do\n            a = base64DecodeChars[255 & e.charCodeAt(o++)];\n        while (o < t && a == -1);\n        if (a == -1)\n            break;\n        d.push(r << 2 | (48 & a) >> 4);\n        do {\n            if (c = 255 & e.charCodeAt(o++), 61 == c)\n                return d;\n            c = base64DecodeChars[c]\n        } while (o < t && c == -1);\n        if (c == -1)\n            break;\n        d.push((15 & a) << 4 | (60 & c) >> 2);\n        do {\n            if (h = 255 & e.charCodeAt(o++), 61 == h)\n                return d;\n            h = base64DecodeChars[h]\n        } while (o < t && h == -1);\n        if (h == -1)\n            break;\n        d.push((3 & c) << 6 | h)\n    }\n    return d\n}\n//stringToBase64 stringToHex stringToBytes\n//base64ToString base64ToHex base64ToBytes\n//               hexToBase64  hexToBytes\n// bytesToBase64 bytesToHex bytesToString\nJava.perform(function () {\n    var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');\n    secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) {\n        showStacks();\n        var result = this.$init(a, b);\n        console.log(\"======================================\");\n        console.log(\"算法名：\" + b + \"|str密钥:\" + bytesToString(a));\n        console.log(\"算法名：\" + b + \"|Hex密钥:\" + bytesToHex(a));\n        return result;\n    }\n    var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec');\n    DESKeySpec.$init.overload('[B').implementation = function (a) {\n        showStacks();\n        var result = this.$init(a);\n        console.log(\"======================================\");\n        var bytes_key_des = this.getKey();\n        console.log(\"des密钥  |str \" + bytesToString(bytes_key_des));\n        console.log(\"des密钥  |hex \" + bytesToHex(bytes_key_des));\n        return result;\n    }\n    DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) {\n        showStacks();\n        var result = this.$init(a, b);\n        console.log(\"======================================\");\n        var bytes_key_des = this.getKey();\n        console.log(\"des密钥  |str \" + bytesToString(bytes_key_des));\n        console.log(\"des密钥  |hex \" + bytesToHex(bytes_key_des));\n        return result;\n    }\n    var mac = Java.use('javax.crypto.Mac');\n    mac.getInstance.overload('java.lang.String').implementation = function (a) {\n        showStacks();\n        var result = this.getInstance(a);\n        console.log(\"======================================\");\n        console.log(\"算法名：\" + a);\n        return result;\n    }\n    mac.update.overload('[B').implementation = function (a) {\n        //showStacks();\n        this.update(a);\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a))\n    }\n    mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {\n        //showStacks();\n        this.update(a, b, c)\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a) + \"|\" + b + \"|\" + c);\n    }\n    mac.doFinal.overload().implementation = function () {\n        //showStacks();\n        var result = this.doFinal();\n        console.log(\"======================================\");\n        console.log(\"doFinal结果: |str  :\"     + bytesToString(result));\n        console.log(\"doFinal结果: |hex  :\"     + bytesToHex(result));\n        console.log(\"doFinal结果: |base64  :\"  + bytesToBase64(result));\n        return result;\n    }\n    mac.doFinal.overload('[B').implementation = function (a) {\n        //showStacks();\n        var result = this.doFinal(a);\n        console.log(\"======================================\");\n        console.log(\"doFinal参数: |str  :\"     + bytesToString(a));\n        console.log(\"doFinal结果: |str  :\"     + bytesToString(result));\n        console.log(\"doFinal结果: |hex  :\"     + bytesToHex(result));\n        console.log(\"doFinal结果: |base64  :\"  + bytesToBase64(result));\n        return result;\n    }\n    var md = Java.use('java.security.MessageDigest');\n    md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) {\n        //showStacks();\n        console.log(\"======================================\");\n        console.log(\"算法名：\" + a);\n        return this.getInstance(a, b);\n    }\n    md.getInstance.overload('java.lang.String').implementation = function (a) {\n        //showStacks();\n        console.log(\"======================================\");\n        console.log(\"算法名：\" + a);\n        return this.getInstance(a);\n    }\n    md.update.overload('[B').implementation = function (a) {\n        //showStacks();\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a))\n        return this.update(a);\n    }\n    md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {\n        //showStacks();\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a) + \"|\" + b + \"|\" + c);\n        return this.update(a, b, c);\n    }\n    md.digest.overload().implementation = function () {\n        //showStacks();\n        console.log(\"======================================\");\n        var result = this.digest();\n        console.log(\"digest结果:\" + bytesToHex(result));\n        console.log(\"digest结果:\" + bytesToBase64(result));\n        return result;\n    }\n    md.digest.overload('[B').implementation = function (a) {\n        //showStacks();\n        console.log(\"======================================\");\n        console.log(\"digest参数:\" + bytesToString(a));\n        var result = this.digest(a);\n        console.log(\"digest结果:\" + bytesToHex(result));\n        console.log(\"digest结果:\" + bytesToBase64(result));\n        return result;\n    }\n    var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec');\n    ivParameterSpec.$init.overload('[B').implementation = function (a) {\n        //showStacks();\n        var result = this.$init(a);\n        console.log(\"======================================\");\n        console.log(\"iv向量: |str:\" + bytesToString(a));\n        console.log(\"iv向量: |hex:\" + bytesToHex(a));\n        return result;\n    }\n    var cipher = Java.use('javax.crypto.Cipher');\n    cipher.getInstance.overload('java.lang.String').implementation = function (a) {\n        //showStacks();\n        var result = this.getInstance(a);\n        console.log(\"======================================\");\n        console.log(\"模式填充:\" + a);\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) {\n        //showStacks();\n        var result = this.init(a, b);\n        console.log(\"======================================\");\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) {\n        //showStacks();\n        var result = this.init(a, b);\n        console.log(\"======================================\");\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) {\n        //showStacks();\n        var result = this.init(a, b, c);\n        console.log(\"======================================\");\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) {\n        //showStacks();\n        var result = this.init(a, b, c);\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) {\n        //showStacks();\n        var result = this.init(a, b, c);\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) {\n        //showStacks();\n        var result = this.init(a, b, c);\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) {\n        //showStacks();\n        var result = this.init(a, b, c, d);\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) {\n        //showStacks();\n        var result = this.update(a, b, c, d);\n        if (N_ENCRYPT_MODE == a)\n        {\n            console.log(\"init  | 加密模式\");\n        }\n        else if(N_DECRYPT_MODE == a)\n        {\n            console.log(\"init  | 解密模式\");\n        }\n        var bytes_key = b.getEncoded();\n        console.log(\"init key:\" + \"|str密钥:\" + bytesToString(bytes_key));\n        console.log(\"init key:\" + \"|Hex密钥:\" + bytesToHex(bytes_key));\n        return result;\n    }\n    cipher.update.overload('[B').implementation = function (a) {\n        //showStacks();\n        var result = this.update(a);\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a));\n        return result;\n    }\n    cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {\n        //showStacks();\n        var result = this.update(a, b, c);\n        console.log(\"======================================\");\n        console.log(\"update:\" + bytesToString(a) + \"|\" + b + \"|\" + c);\n        return result;\n    }\n    cipher.doFinal.overload().implementation = function () {\n        //showStacks();\n        var result = this.doFinal();\n        console.log(\"======================================\");\n        console.log(\"doFinal结果: |str  :\"     + bytesToString(result));\n        console.log(\"doFinal结果: |hex  :\"     + bytesToHex(result));\n        console.log(\"doFinal结果: |base64  :\"  + bytesToBase64(result));\n        return result;\n    }\n    cipher.doFinal.overload('[B').implementation = function (a) {\n        //showStacks();\n        var result = this.doFinal(a);\n        console.log(\"======================================\");\n        console.log(\"doFinal参数: |str  :\"     + bytesToBase64(a));\n        console.log(\"doFinal结果: |str  :\"     + bytesToString(result));\n        console.log(\"doFinal结果: |hex  :\"     + bytesToHex(result));\n        console.log(\"doFinal结果: |base64  :\"  + bytesToBase64(result));\n        return result;\n    }\n    var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec');\n    x509EncodedKeySpec.$init.overload('[B').implementation = function (a) {\n        //showStacks();\n        var result = this.$init(a);\n        console.log(\"======================================\");\n        console.log(\"RSA密钥:\" + bytesToBase64(a));\n        return result;\n    }\n    var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec');\n    rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) {\n        //showStacks();\n        var result = this.$init(a, b);\n        console.log(\"======================================\");\n        //console.log(\"RSA密钥:\" + bytesToBase64(a));\n        console.log(\"RSA密钥N:\" + a.toString(16));\n        console.log(\"RSA密钥E:\" + b.toString(16));\n        return result;\n    }\n    var KeyPairGenerator = Java.use('java.security.KeyPairGenerator');\n    KeyPairGenerator.generateKeyPair.implementation = function ()\n    {\n        //showStacks();\n        var result = this.generateKeyPair();\n        console.log(\"======================================\");\n        var str_private = result.getPrivate().getEncoded();\n        var str_public = result.getPublic().getEncoded();\n        console.log(\"公钥  |hex\" + bytesToHex(str_public));\n        console.log(\"私钥  |hex\" + bytesToHex(str_private));\n        return result;\n    }\n    KeyPairGenerator.genKeyPair.implementation = function ()\n    {\n        //showStacks();\n        var result = this.genKeyPair();\n        console.log(\"======================================\");\n        var str_private = result.getPrivate().getEncoded();\n        var str_public = result.getPublic().getEncoded();\n        console.log(\"公钥  |hex\" + bytesToHex(str_public));\n        console.log(\"私钥  |hex\" + bytesToHex(str_private));\n        return result;\n    }\n});"
  },
  {
    "path": "js/hook_encryption_algo2.js",
    "content": "//打印堆栈\nfunction showStacks() {\n    console.log(\n        Java.use(\"android.util.Log\")\n            .getStackTraceString(\n                Java.use(\"java.lang.Throwable\").$new()\n            )\n    );\n}\nvar ByteString = Java.use(\"com.android.okhttp.okio.ByteString\");\n//输出base64格式数据\nfunction toBase64(tag, data) {\n    console.log(tag + \" Base64: \", ByteString.of(data).base64());\n}\n//输出hex格式数据\nfunction toHex(tag, data) {\n    console.log(tag + \" Hex: \", ByteString.of(data).hex());\n}\n//输出10格式数据\nfunction toUtf8(tag, data) {\n    console.log(tag + \" Utf8: \", ByteString.of(data).utf8());\n}\nvar messageDigest = Java.use(\"java.security.MessageDigest\");\nmessageDigest.update.overload('byte').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.update('byte') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmessageDigest.update.overload('java.nio.ByteBuffer').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.update('java.nio.ByteBuffer') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmessageDigest.update.overload('[B').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.update('[B') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" update data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmessageDigest.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.update('[B', 'int', 'int') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" update data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    console.log(\"start:\", start);\n    console.log(\"length:\", length);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data, start, length);\n}\nmessageDigest.digest.overload().implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.digest() is called!\");\n    var result = this.digest();\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" digest result\";\n    toHex(tag, result);\n    toBase64(tag, result);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\nmessageDigest.digest.overload('[B').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.digest('[B') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" digest data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    var result = this.digest(data);\n    var tags = algorithm + \" digest result\";\n    toHex(tags, result);\n    toBase64(tags, result);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\nmessageDigest.digest.overload('[B', 'int', 'int').implementation = function (data, start, length) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"MessageDigest.digest('[B', 'int', 'int') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" digest data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    var result = this.digest(data, start, length);\n    var tags = algorithm + \" digest result\";\n    toHex(tags, result);\n    toBase64(tags, result);\n    console.log(\"start:\", start);\n    console.log(\"length:\", length);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\nvar mac = Java.use(\"javax.crypto.Mac\");\nmac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.init('java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init(key, AlgorithmParameterSpec);\n}\nmac.init.overload('java.security.Key').implementation = function (key) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.init('java.security.Key') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" init Key\";\n    var keyBytes = key.getEncoded();\n    toUtf8(tag, keyBytes);\n    toHex(tag, keyBytes);\n    toBase64(tag, keyBytes);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init(key);\n}\nmac.update.overload('byte').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.update('byte') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmac.update.overload('java.nio.ByteBuffer').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.update('java.nio.ByteBuffer') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmac.update.overload('[B').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.update('[B') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" update data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nmac.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.update('[B', 'int', 'int') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" update data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    console.log(\"start:\", start);\n    console.log(\"length:\", length);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data, start, length);\n}\nmac.doFinal.overload().implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Mac.doFinal() is called!\");\n    var result = this.doFinal();\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" doFinal result\";\n    toHex(tag, result);\n    toBase64(tag, result);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\n// DES/DESede/AES/RSA\nvar cipher = Java.use(\"javax.crypto.Cipher\");\ncipher.init.overload('int', 'java.security.cert.Certificate').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.cert.Certificate') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key', 'java.security.SecureRandom') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.cert.Certificate', 'java.security.SecureRandom') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" init Key\";\n    var className = JSON.stringify(arguments[1]);\n    if (className.indexOf(\"OpenSSLRSAPrivateKey\") === -1) {\n        var keyBytes = arguments[1].getEncoded();\n        toUtf8(tag, keyBytes);\n        toHex(tag, keyBytes);\n        toBase64(tag, keyBytes);\n    }\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" init Key\";\n    var keyBytes = arguments[1].getEncoded();\n    toUtf8(tag, keyBytes);\n    toHex(tag, keyBytes);\n    toBase64(tag, keyBytes);\n    var tags = algorithm + \" init iv\";\n    var iv = Java.cast(arguments[2], Java.use(\"javax.crypto.spec.IvParameterSpec\"));\n    var ivBytes = iv.getIV();\n    toUtf8(tags, ivBytes);\n    toHex(tags, ivBytes);\n    toBase64(tags, ivBytes);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.init.apply(this, arguments);\n}\ncipher.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('java.nio.ByteBuffer', 'java.nio.ByteBuffer') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.doFinal.apply(this, arguments);\n}\ncipher.doFinal.overload('[B', 'int').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('[B', 'int') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.doFinal.apply(this, arguments);\n}\ncipher.doFinal.overload('[B', 'int', 'int', '[B').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('[B', 'int', 'int', '[B') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.doFinal.apply(this, arguments);\n}\ncipher.doFinal.overload('[B', 'int', 'int', '[B', 'int').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('[B', 'int', 'int', '[B', 'int') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.doFinal.apply(this, arguments);\n}\ncipher.doFinal.overload().implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal() is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.doFinal.apply(this, arguments);\n}\ncipher.doFinal.overload('[B').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('[B') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" doFinal data\";\n    var data = arguments[0];\n    //修改一次\n    toBase64(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    var result = this.doFinal.apply(this, arguments);\n    var tags = algorithm + \" doFinal result\";\n    toHex(tags, result);\n    toBase64(tags, result);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\ncipher.doFinal.overload('[B', 'int', 'int').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Cipher.doFinal('[B', 'int', 'int') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" doFinal data\";\n    var data = arguments[0];\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    var result = this.doFinal.apply(this, arguments);\n    var tags = algorithm + \" doFinal result\";\n    toHex(tags, result);\n    toBase64(tags, result);\n    console.log(\"arguments[1]:\", arguments[1],);\n    console.log(\"arguments[2]:\", arguments[2]);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}\n//签名算法\nvar signature = Java.use(\"java.security.Signature\");\nsignature.update.overload('byte').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Signature.update('byte') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nsignature.update.overload('java.nio.ByteBuffer').implementation = function (data) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Signature.update('java.nio.ByteBuffer') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data);\n}\nsignature.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Signature.update('[B', 'int', 'int') is called!\");\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" update data\";\n    toUtf8(tag, data);\n    toHex(tag, data);\n    toBase64(tag, data);\n    console.log(\"start:\", start);\n    console.log(\"length:\", length);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.update(data, start, length);\n}\nsignature.sign.overload('[B', 'int', 'int').implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Signature.sign('[B', 'int', 'int') is called!\");\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return this.sign.apply(this, arguments);\n}\nsignature.sign.overload().implementation = function () {\n    console.log(\"↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\");\n    console.log(\"Signature.sign() is called!\");\n    var result = this.sign();\n    var algorithm = this.getAlgorithm();\n    var tag = algorithm + \" sign result\";\n    toHex(tag, result);\n    toBase64(tag, result);\n    showStacks();\n    console.log(\"↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\");\n    return result;\n}"
  },
  {
    "path": "js/hook_jni_method_trace.js",
    "content": "function hook_libart() {\n    var symbols = Module.enumerateSymbolsSync(\"libart.so\");\n    var addrGetStringUTFChars = null;\n    var addrNewStringUTF = null;\n    var addrFindClass = null;\n    var addrGetMethodID = null;\n    var addrGetStaticMethodID = null;\n    var addrGetFieldID = null;\n    var addrGetStaticFieldID = null;\n    var addrRegisterNatives = null;\n    var addrCallObjectMethod = null;\n    var addrCallStaticObjectMethod = null;\n    var addrCallVoidMethod = null;\n    for (var i = 0; i < symbols.length; i++) {\n        var symbol = symbols[i];\n        if (\n            symbol.name.indexOf(\"JNI\") >= 0 &&\n            symbol.name.indexOf(\"CheckJNI\") < 0 &&\n            symbol.name.indexOf(\"__va_list\") < 0\n        ) {\n            if (symbol.name.indexOf(\"GetStringUTFChars\") >= 0) {\n                // 觉得频率太大可以注释调\n                //addrGetStringUTFChars = symbol.address;\n                console.log(\"GetStringUTFChars is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"NewStringUTF\") >= 0) {\n                //addrNewStringUTF = symbol.address;\n                console.log(\"NewStringUTF is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"FindClass\") >= 0) {\n                addrFindClass = symbol.address;\n                console.log(\"FindClass is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"GetMethodID\") >= 0) {\n                addrGetMethodID = symbol.address;\n                console.log(\"GetMethodID is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"GetStaticMethodID\") >= 0) {\n                addrGetStaticMethodID = symbol.address;\n                console.log(\"GetStaticMethodID is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"GetFieldID\") >= 0) {\n                addrGetFieldID = symbol.address;\n                console.log(\"GetFieldID is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"GetStaticFieldID\") >= 0) {\n                addrGetStaticFieldID = symbol.address;\n                console.log(\"GetStaticFieldID is at \", symbol.address, symbol.name);\n            } else if (symbol.name.indexOf(\"RegisterNatives\") >= 0) {\n                addrRegisterNatives = symbol.address;\n                console.log(\"RegisterNatives is at \", symbol.address, symbol.name);\n            }else if (symbol.name.indexOf(\"CallObjectMethod\") >= 0 && symbol.name.indexOf(\"art3JNI\") >= 0 && symbol.name.indexOf(\"jmethodIDz\") > 0) {\n                //addrCallObjectMethod = symbol.address;\n                console.log(\"CallObjectMethod is at \", symbol.address, symbol.name);\n            }else if (symbol.name.indexOf(\"CallStaticObjectMethod\") >= 0 && symbol.name.indexOf(\"art3\") >= 0 && symbol.name.endsWith(\"jmethodIDz\")) {\n                addrCallStaticObjectMethod = symbol.address;\n                console.log(\"CallStaticObjectMethod is at \", symbol.address, symbol.name);\n            }else if (symbol.name.indexOf(\"CallVoidMethod\") >= 0 && symbol.name.indexOf(\"art3\") >= 0 && symbol.name.endsWith(\"jmethodIDz\")) {\n                addrCallVoidMethod = symbol.address;\n                console.log(\"CallVoidMethod is at \", symbol.address, symbol.name);\n            }\n        }\n    }\n\n    if (addrCallStaticObjectMethod) {\n        Interceptor.attach(ptr(addrCallStaticObjectMethod), {\n            onEnter: function (args) {\n                var env = args[0];       // JNIEnv*\n                var jclass = args[1];     // Java 类\n                var jmethodID = args[2];  // 方法 ID\n                console.log(\"addrCallStaticObjectMethod [*] CallStaticObjectMethod Hooked!\");\n                console.log(\"addrCallStaticObjectMethod    📌 JNIEnv*: \" + env);\n                console.log(\"addrCallStaticObjectMethod    📌 JClass: \" + jclass);\n                console.log(\" addrCallStaticObjectMethod   📌 JMethodID: \" + jmethodID);\n                // 试图解析方法名（需要配合 GetMethodName ）\n                try {\n                    var methodName = Java.vm.getEnv().getMethodName(jmethodID);\n                    console.log(\"addrCallStaticObjectMethod Method Name: \" + methodName);\n                } catch (err) {\n                    console.log(\"addrCallStaticObjectMethod Failed to get method name\");\n                }\n            },\n            onLeave: function (retval) {\n                console.log(\"addrCallStaticObjectMethod    📤 Return Value: \" + retval);\n            }\n        });\n    } else {\n        console.log(\"[-] CallStaticObjectMethod symbol not found!\");\n    }\n\n    if (addrCallVoidMethod) {\n        Interceptor.attach(ptr(addrCallVoidMethod), {\n            onEnter: function (args) {\n                console.log(\"addrCallVoidMethod [+] Hooked CallVoidMethod\");\n                console.log(\"addrCallVoidMethod    📌 JNIEnv*: \" + args[0]);\n                console.log(\"addrCallVoidMethod    📌 jobject: \" + args[1]);\n                console.log(\"addrCallVoidMethod    📌 jmethodID: \" + args[2]);\n\n                var jclass = Java.cast(args[1], Java.use('java.lang.Object')).getClass();\n                console.log(\"addrCallVoidMethod    📌 Java Class: \" + jclass);\n            },\n            onLeave: function (retval) {\n                console.log(\"addrCallVoidMethod    📤 Return Value: \" + retval);\n            }\n        });\n    } else {\n        console.log(\"[-] CallVoidMethod symbol not found!\");\n    }\n\n    if (addrCallObjectMethod != null) {\n        Interceptor.attach(addrCallObjectMethod, {\n            onEnter: function (args) {\n                console.log(\"[addrCallObjectMethod] called:\");\n\n                // 获取 JNIEnv\n                this.jni_env = args[0];\n                console.log(\"addrCallObjectMethod    📌 JNIEnv: \" + this.jni_env);\n\n                // 获取调用的 Java 对象 (jobject)\n                this.jobject = args[1];\n                console.log(\"addrCallObjectMethod    📌 jobject: \" + this.jobject);\n\n                // 获取方法 ID (jmethodID)\n                this.jmethodID = args[2];\n                console.log(\"addrCallObjectMethod    📌 jmethodID: \" + this.jmethodID);\n\n                // 获取 Java 方法参数\n                this.args_ptr = args[3];  // 可能是变长参数 (depends on overload)\n                console.log(\"addrCallObjectMethod    📌 args_ptr: \" + this.args_ptr);\n\n                // 获取调用的 Java 方法名\n                var jclass = Java.cast(this.jobject, Java.use('java.lang.Object')).getClass();\n                console.log(\"addrCallObjectMethod    📌 Java Class: \" + jclass);\n            },\n            onLeave: function (retval) {\n                if (retval != null) {\n                    // var bytes = Memory.readCString(retval);\n                    // console.log(\"[GetStringUTFChars] result:\" + bytes);\n                }\n            }\n        });\n    }\n    if (addrGetStringUTFChars != null) {\n        Interceptor.attach(addrGetStringUTFChars, {\n            onEnter: function (args) {},\n            onLeave: function (retval) {\n                if (retval != null) {\n                    var bytes = Memory.readCString(retval);\n                    console.log(\"[GetStringUTFChars] result:\" + bytes);\n                }\n            }\n        });\n    }\n    if (addrNewStringUTF) {\n        Interceptor.attach(ptr(addrNewStringUTF), {\n            onEnter: function (args) {\n                this.utf8_string = args[1].readUtf8String(); // 读取传入的 UTF-8 字符串\n                //console.log(\"[*] Hooked NewStringUTF\");\n                if (this.utf8_string) {\n                    console.log(\"addrNewStringUTF    📌 UTF-8 String: \" + this.utf8_string);\n                    // 获取当前线程的调用栈\n                    var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);\n                    // 获取调用该方法的地址（栈中的上一级）\n                    var callerAddress = backtrace[1]; // backtrace[0] 是当前方法的地址，backtrace[1] 是调用者的地址\n                    console.log(\"addrNewStringUTF [*] Caller Address: \" + callerAddress);\n                    var find_module = Process.findModuleByAddress(callerAddress);\n                    if (find_module) {\n                        console.log(\"addrNewStringUTF |--> Module: \" + find_module.name + \" offset:\" + callerAddress.sub(find_module.base));\n                    }\n                }\n\n            },\n            onLeave: function (retval) {\n                //console.log(\" |--> Method ID: \" + retval);\n            }\n        });\n    }\n\n    if (addrFindClass != null) {\n        Interceptor.attach(addrFindClass, {\n            onEnter: function (args) {\n                if (args[1] != null) {\n                    var stackTraceMsg = Thread.backtrace(this.context, Backtracer.ACCURATE)\n                        .map(DebugSymbol.fromAddress).join('\\n');\n                    var name = Memory.readCString(args[1]);\n                    console.log(\"addrFindClass name:\" + name + \" stackTraceMsg:\" + stackTraceMsg);\n                }\n            },\n            onLeave: function (retval) {}\n        });\n    }\n    if (addrGetMethodID != null) {\n        Interceptor.attach(ptr(addrGetMethodID), {\n            onEnter: function (args) {\n\n                this.env = args[0];         // JNIEnv\n                this.jclass = args[1];      // jclass\n                this.method_name = args[2].readCString();  // Method name\n                this.method_sig = args[3].readCString();   // Method signature\n                console.log(\"addrGetMethodID |--> Method Name: \" + this.method_name);\n                if (this.method_name.indexOf(\"sendRequest\") != -1) {\n                    // 获取当前线程的调用栈\n                    var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);\n                    // 获取调用该方法的地址（栈中的上一级）\n                    var callerAddress = backtrace[1]; // backtrace[0] 是当前方法的地址，backtrace[1] 是调用者的地址\n                    console.log(\"addrGetMethodID [*] Caller Address: \" + callerAddress);\n                    var find_module = Process.findModuleByAddress(callerAddress);\n                    console.log(\"\\naddrGetMethodID [*] Hooked GetMethodID\");\n                    console.log(\"addrGetMethodID |--> Method Name: \" + this.method_name);\n                    console.log(\"addrGetMethodID |--> Method Signature: \" + this.method_sig);\n                    console.log(\"addrGetMethodID |--> Module: \" + find_module.name + \" offset:\" + callerAddress.sub(find_module.base));\n                }\n            },\n            onLeave: function (retval) {\n                //console.log(\" |--> Method ID: \" + retval);\n            }\n        });\n    }\n\n    if (addrGetStaticMethodID != null) {\n        Interceptor.attach(addrGetStaticMethodID, {\n            onEnter: function (args) {\n                if (args[2] != null) {\n                    this.method_name = Memory.readCString(args[2]);\n                    if (args[3] != null) {\n                        var sig = Memory.readCString(args[3]);\n                        console.log(\"[addrGetStaticMethodID] name:\" + this.method_name + \", sig:\" + sig);\n                    } else {\n                        console.log(\"[addrGetStaticMethodID] name:\" + this.method_name);\n                    }\n\n                }\n            },\n            onLeave: function (retval) {}\n        });\n    }\n    if (addrGetFieldID != null) {\n        Interceptor.attach(addrGetFieldID, {\n            onEnter: function (args) {\n                if (args[2] != null) {\n                    var name = Memory.readCString(args[2]);\n                    if (args[3] != null) {\n                        var sig = Memory.readCString(args[3]);\n                        console.log(\"[addrGetFieldID] name:\" + name + \", sig:\" + sig);\n                    } else {\n                        console.log(\"[addrGetFieldID] name:\" + name);\n                    }\n\n                }\n            },\n            onLeave: function (retval) {}\n        });\n    }\n    if (addrGetStaticFieldID != null) {\n        Interceptor.attach(addrGetStaticFieldID, {\n            onEnter: function (args) {\n                if (args[2] != null) {\n                    var name = Memory.readCString(args[2]);\n                    if (args[3] != null) {\n                        var sig = Memory.readCString(args[3]);\n                        console.log(\"[addrGetStaticFieldID] name:\" + name + \", sig:\" + sig);\n                    } else {\n                        console.log(\"[addrGetStaticFieldID] name:\" + name);\n                    }\n\n                }\n            },\n            onLeave: function (retval) {}\n        });\n    }\n    if (addrRegisterNatives != null) {\n        Interceptor.attach(addrRegisterNatives, {\n            onEnter: function (args) {\n                console.log(\"[addrRegisterNatives] method_count:\", args[3]);\n                var env = args[0];\n                var java_class = args[1];\n                var class_name = Java.vm.tryGetEnv().getClassName(java_class);\n\n                var methods_ptr = ptr(args[2]);\n\n                var method_count = parseInt(args[3]);\n                for (var i = 0; i < method_count; i++) {\n                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));\n                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));\n                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));\n\n                    var name = Memory.readCString(name_ptr);\n                    var sig = Memory.readCString(sig_ptr);\n                    var find_module = Process.findModuleByAddress(fnPtr_ptr);\n                    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));\n\n                }\n            },\n            onLeave: function (retval) {}\n        });\n    }\n}\n\nsetImmediate(hook_libart);\n"
  },
  {
    "path": "js/hook_proxy_check.js",
    "content": "Java.perform(function () {\n    // Hook检查VPN的类和方法\n    var ConnectivityManager = Java.use('android.net.ConnectivityManager');\n    ConnectivityManager.getNetworkInfo.overload('int').implementation = function (networkType) {\n        console.log('Bypassing VPN detection...');\n        var result = this.getNetworkInfo(networkType);\n        if (networkType === ConnectivityManager.TYPE_VPN.value) {\n            console.log('VPN detected, returning null to bypass detection.');\n            return null;\n        }\n        return result;\n    };\n\n    // Hook检查代理的类和方法\n    var System = Java.use('java.lang.System');\n    System.getProperty.overload('java.lang.String').implementation = function (key) {\n        console.log('Bypassing proxy detection for key: ' + key);\n        if (key === 'http.proxyHost' || key === 'https.proxyHost') {\n            console.log('Proxy detected, returning null to bypass detection.');\n            return null;\n        }\n        var result = this.getProperty(key);\n        console.log(\"key\",key,\"value\",result)\n        return this.getProperty(key);\n    };\n\n    // Hook检查代理的其他方法，如getDefaultProxy\n    var Proxy = Java.use('android.net.Proxy');\n    Proxy.getDefaultHost.implementation = function () {\n        console.log('Bypassing proxy detection in getDefaultHost...');\n        return null;\n    };\n\n    Proxy.getDefaultPort.implementation = function () {\n        console.log('Bypassing proxy detection in getDefaultPort...');\n        return -1; // 返回无效端口\n    };\n\n    console.log('VPN and Proxy detection hooks installed.');\n});"
  },
  {
    "path": "js/hook_register_natives.js",
    "content": "\r\nfunction hook_RegisterNatives() {\r\n    var symbols = Module.enumerateSymbolsSync(\"libart.so\");\r\n    var addrRegisterNatives = null;\r\n    for (var i = 0; i < symbols.length; i++) {\r\n        var symbol = symbols[i];\r\n        \r\n        //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi\r\n        if (symbol.name.indexOf(\"art\") >= 0 &&\r\n                symbol.name.indexOf(\"JNI\") >= 0 && \r\n                symbol.name.indexOf(\"RegisterNatives\") >= 0 && \r\n                symbol.name.indexOf(\"CheckJNI\") < 0) {\r\n            addrRegisterNatives = symbol.address;\r\n            console.log(\"RegisterNatives is at \", symbol.address, symbol.name);\r\n        }\r\n    }\r\n\r\n    if (addrRegisterNatives != null) {\r\n        Interceptor.attach(addrRegisterNatives, {\r\n            onEnter: function (args) {\r\n                console.log(\"[RegisterNatives] method_count:\", args[3]);\r\n                var env = args[0];\r\n                var java_class = args[1];\r\n                var class_name = Java.vm.tryGetEnv().getClassName(java_class);\r\n                //console.log(class_name);\r\n\r\n                var methods_ptr = ptr(args[2]);\r\n\r\n                var method_count = parseInt(args[3]);\r\n                for (var i = 0; i < method_count; i++) {\r\n                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));\r\n                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));\r\n                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));\r\n\r\n                    var name = Memory.readCString(name_ptr);\r\n                    var sig = Memory.readCString(sig_ptr);\r\n                    var find_module = Process.findModuleByAddress(fnPtr_ptr);\r\n                    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));\r\n\r\n                }\r\n            }\r\n        });\r\n    }\r\n}\r\n\r\nsetImmediate(hook_RegisterNatives);\r\n"
  },
  {
    "path": "js/just_trust_me.js",
    "content": "\n\nvar _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}\nvar bbbbbb = \"hooker\";\nvar _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);"
  },
  {
    "path": "js/just_trust_me_for_ios.js",
    "content": "var SecTrustEvaluate_handle =\n    Module.findExportByName('Security', 'SecTrustEvaluate');\nvar SecTrustEvaluateWithError_handle =\n    Module.findExportByName('Security', 'SecTrustEvaluateWithError');\nvar SSL_CTX_set_custom_verify_handle =\n    Module.findExportByName('libboringssl.dylib', 'SSL_CTX_set_custom_verify');\nvar SSL_get_psk_identity_handle =\n    Module.findExportByName('libboringssl.dylib', 'SSL_get_psk_identity');\nvar boringssl_context_set_verify_mode_handle = Module.findExportByName(\n    'libboringssl.dylib', 'boringssl_context_set_verify_mode');\n\nif (SecTrustEvaluateWithError_handle) {\n    var SecTrustEvaluateWithError = new NativeFunction(\n        SecTrustEvaluateWithError_handle, 'int', ['pointer', 'pointer']);\n\n    Interceptor.replace(\n        SecTrustEvaluateWithError_handle,\n        new NativeCallback(function(trust, error) {\n            console.log('[*] Called SecTrustEvaluateWithError()');\n            SecTrustEvaluateWithError(trust, NULL);\n            Memory.writeU8(error, 0);\n            return 1;\n        }, 'int', ['pointer', 'pointer']));\n    console.log('[+] SecTrustEvaluateWithError() hook installed.');\n}\n\nif (SecTrustEvaluate_handle) {\n    var SecTrustEvaluate = new NativeFunction(\n        SecTrustEvaluate_handle, 'int', ['pointer', 'pointer']);\n\n    Interceptor.replace(\n        SecTrustEvaluate_handle, new NativeCallback(function(trust, result) {\n            console.log('[*] Called SecTrustEvaluate()');\n            SecTrustEvaluate(trust, result);\n            Memory.writeU8(result, 1);\n            return 0;\n        }, 'int', ['pointer', 'pointer']));\n    console.log('[+] SecTrustEvaluate() hook installed.');\n}\n\nif (SSL_CTX_set_custom_verify_handle) {\n    var SSL_CTX_set_custom_verify = new NativeFunction(\n        SSL_CTX_set_custom_verify_handle, 'void', ['pointer', 'int', 'pointer']);\n\n    var replaced_callback = new NativeCallback(function(ssl, out) {\n        console.log('[*] Called custom SSL verifier')\n        return 0;\n    }, 'int', ['pointer', 'pointer']);\n\n    Interceptor.replace(\n        SSL_CTX_set_custom_verify_handle,\n        new NativeCallback(function(ctx, mode, callback) {\n            console.log('[*] Called SSL_CTX_set_custom_verify()');\n            SSL_CTX_set_custom_verify(ctx, 0, replaced_callback);\n        }, 'int', ['pointer', 'int', 'pointer']));\n    console.log('[+] SSL_CTX_set_custom_verify() hook installed.')\n}\n\nif (SSL_get_psk_identity_handle) {\n    Interceptor.replace(\n        SSL_get_psk_identity_handle, new NativeCallback(function(ssl) {\n            console.log('[*] Called SSL_get_psk_identity_handle()');\n            return 'notarealPSKidentity';\n        }, 'pointer', ['pointer']));\n    console.log('[+] SSL_get_psk_identity() hook installed.')\n}\n\nif (boringssl_context_set_verify_mode_handle) {\n    var boringssl_context_set_verify_mode = new NativeFunction(\n        boringssl_context_set_verify_mode_handle, 'int', ['pointer', 'pointer']);\n\n    Interceptor.replace(\n        boringssl_context_set_verify_mode_handle,\n        new NativeCallback(function(a, b) {\n            console.log('[*] Called boringssl_context_set_verify_mode()');\n            return 0;\n        }, 'int', ['pointer', 'pointer']));\n    console.log('[+] boringssl_context_set_verify_mode() hook installed.')\n}"
  },
  {
    "path": "js/keystore_dump.js",
    "content": "//在https双向认证的情况下，dump客户端证书为p12. 证书密码: hooker\nvar password = \"hooker\";\n\n\n\nfunction dateFormat(fmt, date) {\n    let ret;\n    const opt = {\n        \"Y+\": date.getFullYear().toString(),\n        // 年\n        \"m+\": (date.getMonth() + 1).toString(),\n        // 月\n        \"d+\": date.getDate().toString(),\n        // 日\n        \"H+\": date.getHours().toString(),\n        // 时\n        \"M+\": date.getMinutes().toString(),\n        // 分\n        \"S+\": date.getSeconds().toString() // 秒\n    };\n    for (let k in opt) {\n        ret = new RegExp(\"(\" + k + \")\").exec(fmt);\n        if (ret) {\n            fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, \"0\")))\n        };\n    };\n    return fmt;\n}\n\nfunction random(min, max) {\n    return Math.floor(Math.random() * (max - min)) + min;\n}\n\nfunction getNowTime() {\n    return dateFormat(\"YYYY_mm_dd_HH_MM_SS\", new Date()) + \"_\" + random(1, 100);\n}\n\nfunction getPackageName() {\n    var currentApplication = Java.use('android.app.ActivityThread').currentApplication();\n    var context = currentApplication.getApplicationContext();\n    return context.getPackageName();\n};\n\nfunction newMethodBeat(text, executor) {\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var processClz = Java.use(\"android.os.Process\");\n    var currentThread = threadClz.currentThread();\n    var beat = new Object();\n    beat.invokeId = Math.random().toString(36).slice( - 8);\n    beat.executor = executor;\n    beat.myPid = processClz.myPid();\n    beat.threadId = currentThread.getId();\n    beat.threadName = currentThread.getName();\n    beat.text = text;\n    beat.startTime = new Date().getTime();\n    beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20);\n    return beat;\n};\n\nfunction printBeat(beat) {\n    var str = (\"------------pid:\" + beat.myPid + \",startFlag:\" + beat.invokeId + \",objectHash:\"+beat.executor+\",thread(id:\" + beat.threadId +\",name:\" + beat.threadName + \"),timestamp:\" + beat.startTime+\"---------------\\n\");\n    str += beat.text + \"\\n\";\n    str += beat.stackInfo;\n    str += (\"------------endFlag:\" + beat.invokeId + \",usedtime:\" + (new Date().getTime() - beat.startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nfunction dump2sdcard(pri, p7, filePath) {\n    console.log(\"dump:\" + filePath);\n    var X509CertificateClass = Java.use(\"java.security.cert.X509Certificate\");\n    var myX509 = Java.cast(p7, X509CertificateClass);\n    var chain = Java.array(\"java.security.cert.X509Certificate\", [myX509]);\n    var ks = Java.use(\"java.security.KeyStore\").getInstance(\"PKCS12\", \"BC\");\n    ks.load(null, null);\n    ks.setKeyEntry(\"client\", pri, Java.use('java.lang.String').$new(password).toCharArray(), chain);\n    try {\n        var out = Java.use(\"java.io.FileOutputStream\").$new(filePath);\n        ks.store(out, Java.use('java.lang.String').$new(password).toCharArray());\n    } catch(error) {\n        console.log(error);\n    }\n}\n\n\n\nJava.perform(function() {\n    var packageName = getPackageName();\n    console.log(\"在https双向认证的情况下，dump客户端证书为p12. 存储位置:/data/user/0/\"+packageName+\"/client_keystore_{nowtime}.p12 证书密码: hooker\");\n    Java.use(\"java.security.KeyStore$PrivateKeyEntry\").getPrivateKey.implementation = function() {\n    \t var executor = this.hashCode();\n        var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getPrivateKey()';\n        var beat = newMethodBeat(beatText, executor);\n        var result = this.getPrivateKey();\n        let filePath = '/data/user/0/' + packageName + \"/client_keystore_\" + \"_\" + getNowTime() + '.p12';\n        dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath);\n        printBeat(beat);\n        return result;\n    }\n    Java.use(\"java.security.KeyStore$PrivateKeyEntry\").getCertificateChain.implementation = function() {\n    var executor = this.hashCode();\n        var beatText = 'public java.security.cert.Certificate java.security.KeyStore$PrivateKeyEntry.getCertificate()';\n        var beat = newMethodBeat(beatText, executor);\n        var result = this.getCertificateChain();\n        let filePath = '/data/user/0/' + packageName + \"/client_keystore_\" + getNowTime() + '.p12';\n        dump2sdcard(this.getPrivateKey(), this.getCertificate(), filePath);\n        return result;\n    }\n})\n"
  },
  {
    "path": "js/object_store.js",
    "content": "function loadDexfile(dexfile) {\n    Java.perform(function() {\n        Java.openClassFile(dexfile).load();\n    });\n};\n\nloadDexfile('/data/local/tmp/radar.dex');\n\n\nfunction log(str) {\n    console.log(str);\n};\n\nfunction getBaseContext() {\n    var currentApplication = Java.use('android.app.ActivityThread').currentApplication();\n    var context = currentApplication.getApplicationContext();\n    return context; //Java.scheduleOnMainThread(fn):\n};\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while (new Date().getTime() < startTime) {}\n};\n\nfunction fastTojson(javaObject) {\n    var JSONClz = Java.use(\"gz.com.alibaba.fastjson.JSON\");\n    return JSONClz.toJSONString(javaObject);\n};\n\nfunction object2Json(objectId) {\n\tvar radarPropertiesClz = Java.use(\"gz.radar.objects.ObjectsStore\");\n\tvar obj = radarPropertiesClz.getObject(objectId+\"\");\n\tif (obj) {\n\t\tlog(fastTojson(obj));\n\t}else{\n\t\tlog(\"Not found this object.\");\n\t}\n}\n\nfunction object2String(objectId) {\n\tvar radarPropertiesClz = Java.use(\"gz.radar.objects.ObjectsStore\");\n\tvar obj = radarPropertiesClz.getObject(objectId+\"\");\n\tif (obj) {\n\t\tlog(obj.toString());\n\t}else{\n\t\tlog(\"Not found this object.\");\n\t}\n}\n\nfunction getObject(objectId) {\n\tvar radarPropertiesClz = Java.use(\"gz.radar.objects.ObjectsStore\");\n\tvar obj = radarPropertiesClz.getObject(objectId+\"\");\n\treturn obj;\n}\n\n\nfunction getField(javaObject, fieldName) {\n    var X = Java.use(\"gz.util.X\");\n    return X.getField(javaObject, fieldName);\n};\n\n"
  },
  {
    "path": "js/param_hook.js",
    "content": "//crack by com.smile.gifmaker 7.5.40.14691\n//java.util.HashMap:put\nfunction loadDexfile(dexfile) {\n    Java.perform(function() {\n        Java.openClassFile(dexfile).load();\n        console.log(\"load \" + dexfile);\n    });\n};\n\nfunction checkLoadDex(className, dexfile) {\n    Java.perform(function() {\n        if (!classExists(className)) {\n            Java.openClassFile(dexfile).load();\n            console.log(\"load \" + dexfile);\n        }\n    });\n};\n\nfunction classExists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\nfunction getClassName(obj) {\n    if (obj.getClass) {\n        return obj.getClass().getName();\n    }\n    var javaObject = Java.use(\"java.lang.Object\");\n    return Java.cast(obj, javaObject).getClass().getName();\n}\n\n//str1是否包含str2，str2可用正则表示\nfunction contains(str1, str2) {\n    var reg = RegExp(eval(\"/\" + str2 + \"/\"));\n    if (str1 && str1.match && str1.match(reg)) {\n        return true;\n    } else {\n        return false;\n    }\n};\n\n//创建ArrayList对象用这个方法就好了\nfunction newArrayList() {\n    var ArrayListClz = Java.use('java.util.ArrayList');\n    return ArrayListClz.$new();\n}\n\n//创建HashSet对象用这个方法就好了\nfunction newHashSet() {\n    var HashSetClz = Java.use('java.util.HashSet');\n    return HashSetClz.$new();\n}\n\n//创建HashMap对象用这个方法就好了\nfunction newHashMap() {\n    var HashMapClz = Java.use('java.util.HashMap');\n    return HashMapClz.$new();\n}\n\nfunction methodInBeat(invokeId, timestamp, methodName, executor) {\n    var startTime = timestamp;\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var currentThread = threadClz.currentThread();\n    var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new());\n    var str = (\"------------startFlag:\" + invokeId + \",objectHash:\" + executor + \",thread(id:\" + currentThread.getId() + \",name:\" + currentThread.getName() + \"),timestamp:\" + startTime + \"---------------\\n\");\n    str += methodName + \"\\n\";\n    str += stackInfo.substring(20);\n    str += (\"------------endFlag:\" + invokeId + \",usedtime:\" + (new Date().getTime() - startTime) + \"---------------\\n\");\n    console.log(str);\n};\n\nfunction log(str) {\n    console.log(str);\n};\n\n//虽然我们习惯用fastjson一行将对象转成json字符串，但是Android Library里面自带了一个gson可以做到 只是sdk没有暴露出来，很多人不知道。在frida中所有代码都是透明的，你随便调......\nfunction toJson(javaObject) {\n    var gsonClz = Java.use(\"com.google.gson.Gson\");\n    var toJsonMethod = gsonClz.toJson.overload(\"java.lang.Object\");\n    return toJsonMethod.call(gsonClz.$new(), javaObject);\n};\n\nfunction getBaseContext() {\n    var currentApplication = Java.use('android.app.ActivityThread').currentApplication();\n    var context = currentApplication.getApplicationContext();\n    return context; //Java.scheduleOnMainThread(fn):\n};\n\nfunction sleep(time) {\n    var startTime = new Date().getTime() + parseInt(time, 10);\n    while (new Date().getTime() < startTime) {}\n};\n\nvar containRegExps = new Array(RegExp(/NStokensig/))\n\nfunction check(stringJavaObject) {\n    var str = stringJavaObject.toString();\n    if (! (str && str.match)) {\n        return false;\n    }\n    for (var i = 0; i < containRegExps.length; i++) {\n        if (!str.match(containRegExps[i])) {\n            return false;\n        }\n    }\n    return true;\n}\n\nJava.perform(function() {\n    var java_util_HashMap_clz = Java.use('java.util.HashMap');\n    var java_util_HashMap_clz_method_put_wh2m = java_util_HashMap_clz.put.overload('java.lang.Object', 'java.lang.Object');\n    java_util_HashMap_clz_method_put_wh2m.implementation = function(v0, v1) {\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var startTime = new Date().getTime();\n        var executor = this.hashCode();\n        var ret = java_util_HashMap_clz_method_put_wh2m.call(this, v0, v1);\n        if (check(v0)) {\n            log(v0 + \"=\" + v1);\n            methodInBeat(invokeId, startTime, 'public java.lang.Object java.util.HashMap.put(java.lang.Object,java.lang.Object)', executor);\n        }\n        return ret;\n    };\n    var android_util_Pair_clz = Java.use('android.util.Pair');\n    var android_util_Pair_clz_init_74e4 = android_util_Pair_clz.$init.overload('java.lang.Object', 'java.lang.Object');\n    android_util_Pair_clz_init_74e4.implementation = function(v0, v1) {\n        var invokeId = Math.random().toString(36).slice( - 8);\n        var startTime = new Date().getTime();\n        var executor = this.hashCode();\n        var returnObj = android_util_Pair_clz_init_74e4.call(this, v0, v1);\n        var v0ClassName = getClassName(v0);\n        if (v0ClassName == \"java.lang.String\" && check(v0.toString())) {\n            log(v0 + \"=\" + v1);\n            methodInBeat(invokeId, startTime, 'public android.util.Pair(java.lang.Object,java.lang.Object)', executor);\n        }\n        return returnObj;\n    };\n});"
  },
  {
    "path": "js/r0capture.js",
    "content": "/**\n   * Initializes 'addresses' dictionary and NativeFunctions.\n   */\n\"use strict\";\nrpc.exports = {\n  setssllib: function (name) {\n    console.log(\"setSSLLib => \" + name);\n    libname = name;\n    initializeGlobals();\n    return;\n  }\n};\n\nvar addresses = {};\nvar SSL_get_fd = null;\nvar SSL_get_session = null;\nvar SSL_SESSION_get_id = null;\nvar getpeername = null;\nvar getsockname = null;\nvar ntohs = null;\nvar ntohl = null;\nvar SSLstackwrite = null;\nvar SSLstackread = null;\n\nvar libname = \"*libssl*\";\n\nfunction uuid(len, radix) {\n  var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');\n  var uuid = [], i;\n  radix = radix || chars.length;\n\n  if (len) {\n    // Compact form\n    for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];\n  } else {\n    // rfc4122, version 4 form\n    var r;\n\n    // rfc4122 requires these characters\n    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';\n    uuid[14] = '4';\n\n    // Fill in random data. At i==19 set the high bits of clock sequence as\n    // per rfc4122, sec. 4.1.5\n    for (i = 0; i < 36; i++) {\n      if (!uuid[i]) {\n        r = 0 | Math.random() * 16;\n        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];\n      }\n    }\n  }\n\n  return uuid.join('');\n}\nfunction return_zero(args) {\n  return 0;\n}\nfunction initializeGlobals() {\n  var resolver = new ApiResolver(\"module\");\n  var exps = [\n    [Process.platform == \"darwin\" ? \"*libboringssl*\" : \"*libssl*\", [\"SSL_read\", \"SSL_write\", \"SSL_get_fd\", \"SSL_get_session\", \"SSL_SESSION_get_id\"]], // for ios and Android\n    [Process.platform == \"darwin\" ? \"*libsystem*\" : \"*libc*\", [\"getpeername\", \"getsockname\", \"ntohs\", \"ntohl\"]]\n  ];\n  // console.log(exps)\n  for (var i = 0; i < exps.length; i++) {\n    var lib = exps[i][0];\n    var names = exps[i][1];\n    for (var j = 0; j < names.length; j++) {\n      var name = names[j];\n      // console.log(\"exports:\" + lib + \"!\" + name)\n      var matches = resolver.enumerateMatchesSync(\"exports:\" + lib + \"!\" + name);\n      if (matches.length == 0) {\n        if (name == \"SSL_get_fd\") {\n          addresses[\"SSL_get_fd\"] = 0;\n          continue;\n        }\n        throw \"Could not find \" + lib + \"!\" + name;\n      }\n      else if (matches.length != 1) {\n        // Sometimes Frida returns duplicates.\n        var address = 0;\n        var s = \"\";\n        var duplicates_only = true;\n        for (var k = 0; k < matches.length; k++) {\n          if (s.length != 0) {\n            s += \", \";\n          }\n          s += matches[k].name + \"@\" + matches[k].address;\n          if (address == 0) {\n            address = matches[k].address;\n          }\n          else if (!address.equals(matches[k].address)) {\n            duplicates_only = false;\n          }\n        }\n        if (!duplicates_only) {\n          throw \"More than one match found for \" + lib + \"!\" + name + \": \" + s;\n        }\n      }\n      addresses[name] = matches[0].address;\n    }\n  }\n  if (addresses[\"SSL_get_fd\"] == 0) {\n    SSL_get_fd = return_zero;\n  } else {\n    SSL_get_fd = new NativeFunction(addresses[\"SSL_get_fd\"], \"int\", [\"pointer\"]);\n  }\n  SSL_get_session = new NativeFunction(addresses[\"SSL_get_session\"], \"pointer\", [\"pointer\"]);\n  SSL_SESSION_get_id = new NativeFunction(addresses[\"SSL_SESSION_get_id\"], \"pointer\", [\"pointer\", \"pointer\"]);\n  getpeername = new NativeFunction(addresses[\"getpeername\"], \"int\", [\"int\", \"pointer\", \"pointer\"]);\n  getsockname = new NativeFunction(addresses[\"getsockname\"], \"int\", [\"int\", \"pointer\", \"pointer\"]);\n  ntohs = new NativeFunction(addresses[\"ntohs\"], \"uint16\", [\"uint16\"]);\n  ntohl = new NativeFunction(addresses[\"ntohl\"], \"uint32\", [\"uint32\"]);\n}\ninitializeGlobals();\n\nfunction ipToNumber(ip) {\n  var num = 0;\n  if (ip == \"\") {\n    return num;\n  }\n  var aNum = ip.split(\".\");\n  if (aNum.length != 4) {\n    return num;\n  }\n  num += parseInt(aNum[0]) << 0;\n  num += parseInt(aNum[1]) << 8;\n  num += parseInt(aNum[2]) << 16;\n  num += parseInt(aNum[3]) << 24;\n  num = num >>> 0;//这个很关键，不然可能会出现负数的情况\n  return num;\n}\n\n/**\n * Returns a dictionary of a sockfd's \"src_addr\", \"src_port\", \"dst_addr\", and\n * \"dst_port\".\n * @param {int} sockfd The file descriptor of the socket to inspect.\n * @param {boolean} isRead If true, the context is an SSL_read call. If\n *     false, the context is an SSL_write call.\n * @return {dict} Dictionary of sockfd's \"src_addr\", \"src_port\", \"dst_addr\",\n *     and \"dst_port\".\n */\nfunction getPortsAndAddresses(sockfd, isRead) {\n  var message = {};\n  var src_dst = [\"src\", \"dst\"];\n  for (var i = 0; i < src_dst.length; i++) {\n    if ((src_dst[i] == \"src\") ^ isRead) {\n      var sockAddr = Socket.localAddress(sockfd)\n    }\n    else {\n      var sockAddr = Socket.peerAddress(sockfd)\n    }\n    if (sockAddr == null) {\n      // 网络超时or其他原因可能导致socket被关闭\n      message[src_dst[i] + \"_port\"] = 0\n      message[src_dst[i] + \"_addr\"] = 0\n    } else {\n      message[src_dst[i] + \"_port\"] = (sockAddr.port & 0xFFFF)\n      message[src_dst[i] + \"_addr\"] = ntohl(ipToNumber(sockAddr.ip.split(\":\").pop()))\n    }\n  }\n  return message;\n}\n/**\n * Get the session_id of SSL object and return it as a hex string.\n * @param {!NativePointer} ssl A pointer to an SSL object.\n * @return {dict} A string representing the session_id of the SSL object's\n *     SSL_SESSION. For example,\n *     \"59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336\".\n */\nfunction getSslSessionId(ssl) {\n  var session = SSL_get_session(ssl);\n  if (session == 0) {\n    return 0;\n  }\n  var len = Memory.alloc(4);\n  var p = SSL_SESSION_get_id(session, len);\n  len = Memory.readU32(len);\n  var session_id = \"\";\n  for (var i = 0; i < len; i++) {\n    // Read a byte, convert it to a hex string (0xAB ==> \"AB\"), and append\n    // it to session_id.\n    session_id +=\n      (\"0\" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2);\n  }\n  return session_id;\n}\n\nInterceptor.attach(addresses[\"SSL_read\"],\n  {\n    onEnter: function (args) {\n      var message = getPortsAndAddresses(SSL_get_fd(args[0]), true);\n      message[\"ssl_session_id\"] = getSslSessionId(args[0]);\n      message[\"function\"] = \"SSL_read\";\n      message[\"stack\"] = SSLstackread;\n      this.message = message;\n      this.buf = args[1];\n    },\n    onLeave: function (retval) {\n      retval |= 0; // Cast retval to 32-bit integer.\n      if (retval <= 0) {\n        return;\n      }\n      send(this.message, Memory.readByteArray(this.buf, retval));\n    }\n  });\n\nInterceptor.attach(addresses[\"SSL_write\"],\n  {\n    onEnter: function (args) {\n      var message = getPortsAndAddresses(SSL_get_fd(args[0]), false);\n      message[\"ssl_session_id\"] = getSslSessionId(args[0]);\n      message[\"function\"] = \"SSL_write\";\n      message[\"stack\"] = SSLstackwrite;\n      send(message, Memory.readByteArray(args[1], parseInt(args[2])));\n    },\n    onLeave: function (retval) {\n    }\n  });\n\nif (Java.available) {\n  Java.perform(function () {\n    function storeP12(pri, p7, p12Path, p12Password) {\n      var X509Certificate = Java.use(\"java.security.cert.X509Certificate\")\n      var p7X509 = Java.cast(p7, X509Certificate);\n      var chain = Java.array(\"java.security.cert.X509Certificate\", [p7X509])\n      var ks = Java.use(\"java.security.KeyStore\").getInstance(\"PKCS12\", \"BC\");\n      ks.load(null, null);\n      ks.setKeyEntry(\"client\", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);\n      try {\n        var out = Java.use(\"java.io.FileOutputStream\").$new(p12Path);\n        ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())\n      } catch (exp) {\n        console.log(exp)\n      }\n    }\n    //在服务器校验客户端的情形下，帮助dump客户端证书，并保存为p12的格式，证书密码为r0ysue\n    Java.use(\"java.security.KeyStore$PrivateKeyEntry\").getPrivateKey.implementation = function () {\n      var result = this.getPrivateKey()\n      var packageName = Java.use(\"android.app.ActivityThread\").currentApplication().getApplicationContext().getPackageName();\n      storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');\n      var message = {};\n      message[\"function\"] = \"dumpClinetCertificate=>\" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + '   pwd: r0ysue';\n      message[\"stack\"] = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new());\n      var data = Memory.alloc(1);\n      send(message, Memory.readByteArray(data, 1))\n      return result;\n    }\n    Java.use(\"java.security.KeyStore$PrivateKeyEntry\").getCertificateChain.implementation = function () {\n      var result = this.getCertificateChain()\n      var packageName = Java.use(\"android.app.ActivityThread\").currentApplication().getApplicationContext().getPackageName();\n      storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');\n      var message = {};\n      message[\"function\"] = \"dumpClinetCertificate=>\" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + '   pwd: r0ysue';\n      message[\"stack\"] = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new());\n      var data = Memory.alloc(1);\n      send(message, Memory.readByteArray(data, 1))\n      return result;\n    }\n\n    //SSLpinning helper 帮助定位证书绑定的关键代码a\n    Java.use(\"java.io.File\").$init.overload('java.io.File', 'java.lang.String').implementation = function (file, cert) {\n      var result = this.$init(file, cert)\n      var stack = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new());\n      if (file.getPath().indexOf(\"cacert\") >= 0 && stack.indexOf(\"X509TrustManagerExtensions.checkServerTrusted\") >= 0) {\n        var message = {};\n        message[\"function\"] = \"SSLpinning position locator => \" + file.getPath() + \" \" + cert;\n        message[\"stack\"] = stack;\n        var data = Memory.alloc(1);\n        send(message, Memory.readByteArray(data, 1))\n      }\n      return result;\n    }\n\n\n    Java.use(\"java.net.SocketOutputStream\").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) {\n      var result = this.socketWrite0(fd, bytearry, offset, byteCount);\n      var message = {};\n      message[\"function\"] = \"HTTP_send\";\n      message[\"ssl_session_id\"] = \"\";\n      message[\"src_addr\"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(\":\")[0]).split(\"/\").pop()));\n      message[\"src_port\"] = parseInt(this.socket.value.getLocalPort().toString());\n      message[\"dst_addr\"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(\":\")[0]).split(\"/\").pop()));\n      message[\"dst_port\"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(\":\").pop());\n      message[\"stack\"] = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n      var ptr = Memory.alloc(byteCount);\n      for (var i = 0; i < byteCount; ++i)\n        Memory.writeS8(ptr.add(i), bytearry[offset + i]);\n      send(message, Memory.readByteArray(ptr, byteCount))\n      return result;\n    }\n    Java.use(\"java.net.SocketInputStream\").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) {\n      var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout);\n      var message = {};\n      message[\"function\"] = \"HTTP_recv\";\n      message[\"ssl_session_id\"] = \"\";\n      message[\"src_addr\"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(\":\")[0]).split(\"/\").pop()));\n      message[\"src_port\"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(\":\").pop());\n      message[\"dst_addr\"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(\":\")[0]).split(\"/\").pop()));\n      message[\"dst_port\"] = parseInt(this.socket.value.getLocalPort());\n      message[\"stack\"] = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n      if (result > 0) {\n        var ptr = Memory.alloc(result);\n        for (var i = 0; i < result; ++i)\n          Memory.writeS8(ptr.add(i), bytearry[offset + i]);\n        send(message, Memory.readByteArray(ptr, result))\n      }\n      return result;\n    }\n\n    if (parseFloat(Java.androidVersion)  > 8) {\n      Java.use(\"com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream\").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {\n        var result = this.write(bytearry, int1, int2);\n        SSLstackwrite = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n        return result;\n      }\n      Java.use(\"com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream\").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {\n        var result = this.read(bytearry, int1, int2);\n        SSLstackread = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n        return result;\n      }\n    }\n    else {\n      Java.use(\"com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream\").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {\n        var result = this.write(bytearry, int1, int2);\n        SSLstackwrite = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n        return result;\n      }\n      Java.use(\"com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream\").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) {\n        var result = this.read(bytearry, int1, int2);\n        SSLstackread = Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Throwable\").$new()).toString();\n        return result;\n      }\n\n    }\n  }\n\n  )\n}\n\nrpc.exports = {\n    cleanup: function () {\n        // 清理所有拦截器\n        Interceptor.detachAll();\n        // 如果你设置了定时器或 Stalker，也可以清理\n        // Stalker.unfollow();\n        // clearInterval(...);\n    }\n};"
  },
  {
    "path": "js/replace_dlsym_get_pthread_create.js",
    "content": "var pthread_create_ptr = Module.getExportByName(null, \"pthread_create\");\n\n// 备份原始函数\nvar original_pthread_create = new NativeFunction(pthread_create_ptr, 'int', ['pointer', 'pointer', 'pointer', 'pointer']);\n\nvar my_pthread_create = new NativeCallback(function (thread_ptr, attr_ptr, start_routine, arg_ptr) {\n    console.log(\"[*] 自定义 pthread_create 被调用！\");\n    console.log(\"    thread_ptr:     \" + thread_ptr);\n    console.log(\"    attr_ptr:       \" + attr_ptr);\n    console.log(\"    start_routine:  \" + start_routine);\n    console.log(\"    arg_ptr:        \" + arg_ptr);\n    var find_module = Process.findModuleByAddress(start_routine);\n    console.log(\"这是pthread_create传入的函数地址，你可以再去hook这个函数看看BLR X8指令的位置，然后NOP掉--> Module: \" + find_module.name + \" offset:\" + start_routine.sub(find_module.base));\n    // 你可以选择真的创建一个线程（高级场景），或者只打印/屏蔽掉\n    // 此处直接返回成功状态\n    return 0;\n}, 'int', ['pointer', 'pointer', 'pointer', 'pointer']);\n\nInterceptor.attach(Module.getExportByName(null, \"dlsym\"), {\n    onEnter(args) {\n        this.symbol = Memory.readUtf8String(args[1]);\n    },\n    onLeave(retval) {\n        if (this.symbol.indexOf(\"pthread_create\") !== -1) {\n            console.log(\"[*] dlsym loaded pthread_create, addr:\", retval);\n            // 获取当前线程的调用栈\n            var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);\n            // 获取调用该方法的地址（栈中的上一级）\n            var callerAddress = backtrace[0];\n            var find_module = Process.findModuleByAddress(callerAddress);\n            if (find_module && find_module.name.indexOf(\"libmsaoaidsec.so\") !== -1) {\n                // console.log('\\nBacktrace:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE)\n                //     .map(DebugSymbol.fromAddress).join('\\n'));\n                console.log(\"invoke dlsym |--> Module: \" + find_module.name + \" offset:\" + callerAddress.sub(find_module.base));\n                // 👇 替换返回值为我们自定义的 pthread_create\n                retval.replace(ptr(my_pthread_create));\n            }\n        }\n    }\n});\n\n// clone(Linux下线程创建使用的系统调用)、pthread create 的相关库函数pthread join 等。这些函数也可能涉及线程的创建和管理，可以间接达到相同的效果。\n// var clone = Module.findExportByName('libc.so', 'clone');\n// Interceptor.attach(clone, {\n//     onEnter: function(args) {\n//         // args[3] 子线程的栈地址。如果这个值为 0，可能意味着没有指定栈地址\n//         if(args[3] != 0){\n//             var callerAddress = args[3].add(96).readPointer()\n//             var find_module = Process.findModuleByAddress(callerAddress);\n//             if (find_module && find_module.name.indexOf(\"libmsaoaidsec.so\") !== -1) {\n//                 // console.log('\\nBacktrace:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE)\n//                 //     .map(DebugSymbol.fromAddress).join('\\n'));\n//                 console.log(\"hook_clone invoke Module: \" + find_module.name + \" offset:\" + callerAddress.sub(find_module.base));\n//                 // 👇 替换返回值为我们自定义的 pthread_create\n//                 args[3] = ptr(my_pthread_create);\n//             }\n//         }\n//     },\n//     onLeave: function(retval) {\n//     }\n// });\n//\n// function anti_check_frida_feature() {\n//     var pt_strstr = Module.findExportByName(\"libc.so\", 'strstr');\n//     var pt_strcmp = Module.findExportByName(\"libc.so\", 'strcmp');\n//\n//     Interceptor.attach(pt_strstr, {\n//         onEnter: function (args) {\n//             var str1 = args[0].readCString();\n//             var str2 = args[1].readCString();\n//             if (\n//                 str2.indexOf(\"REJECT\") !== -1 ||\n//                 str2.indexOf(\"tmp\") !== -1 ||\n//                 str2.indexOf(\"frida\") !== -1 ||\n//                 str2.indexOf(\"gum-js-loop\") !== -1 ||\n//                 str2.indexOf(\"gmain\") !== -1 ||\n//                 str2.indexOf(\"linjector\") !== -1\n//             ) {\n//                 //console.log(\"strstr-->\", str1, str2);\n//                 this.hook = true;\n//             }\n//         }, onLeave: function (retval) {\n//             if (this.hook) {\n//                 retval.replace(0);\n//             }\n//         }\n//     });\n//\n//     Interceptor.attach(pt_strcmp, {\n//         onEnter: function (args) {\n//             var str1 = args[0].readCString();\n//             var str2 = args[1].readCString();\n//             if (\n//                 str2.indexOf(\"REJECT\") !== -1 ||\n//                 str2.indexOf(\"tmp\") !== -1 ||\n//                 str2.indexOf(\"frida\") !== -1 ||\n//                 str2.indexOf(\"gum-js-loop\") !== -1 ||\n//                 str2.indexOf(\"gmain\") !== -1 ||\n//                 str2.indexOf(\"linjector\") !== -1\n//             ) {\n//                 //console.log(\"strcmp-->\", str1, str2);\n//                 this.hook = true;\n//             }\n//         }, onLeave: function (retval) {\n//             if (this.hook) {\n//                 retval.replace(0);\n//             }\n//         }\n//     })\n// }\n//\n// setImmediate(anti_check_frida_feature)"
  },
  {
    "path": "js/rpc.js",
    "content": "function mkdirs(dirpath) {\n    var FileClz = Java.use(\"java.io.File\");\n    var file = FileClz.$new(dirpath);\n    if (!file.exists()) {\n        file.mkdirs();\n    }\n}\n\nfunction writeFileAsBase64Content(filepath, base64) {\n    try {\n        var FileUtilsClz = Java.use(\"android.os.FileUtils\");\n        var StringClz = Java.use('java.lang.String');\n        var Base64Clz = Java.use(\"android.util.Base64\");\n        var ByteArrayInputStreamClz = Java.use(\"java.io.ByteArrayInputStream\");\n        var FileOutputStreamClz = Java.use(\"java.io.FileOutputStream\");\n        var FileClz = Java.use(\"java.io.File\");\n        var distFilepath = FileClz.$new(filepath);\n        mkdirs(distFilepath.getParent());\n        var javaBase64String = StringClz.$new(base64);\n        var getBytesMehtod = StringClz.getBytes.overload('java.lang.String');\n        var bytes = getBytesMehtod.call(javaBase64String, 'UTF-8');\n        var decodeMethod = Base64Clz.decode.overload('[B', 'int');\n        var originalBinary = decodeMethod.call(Base64Clz, bytes, 0);\n        var bais = ByteArrayInputStreamClz.$new(originalBinary);\n        if (FileUtilsClz.copy) {\n            var copyMehtod = FileUtilsClz.copy.overload('java.io.InputStream', 'java.io.OutputStream');\n            var fos = FileOutputStreamClz.$new(distFilepath);\n            copyMehtod.call(FileUtilsClz, bais, fos);\n        } else if (FileUtilsClz.copyToFile) {\n            var copyMehtod = FileUtilsClz.copyToFile.overload('java.io.InputStream', 'java.io.File');\n            copyMehtod.call(FileUtilsClz, bais, distFilepath);\n        }\n    } catch(err) {\n        console.warn(err);\n    }\n};\n\nfunction checkFile(filepath, checkLength) {\n    var FileClz = Java.use(\"java.io.File\");\n    var file = FileClz.$new(filepath);\n    return file.exists() && file.length() == checkLength;\n};\n\n\nfunction discoverClass(className) {\n    if (!className) {\n        return;\n    }\n    var radarClz = Java.use(\"gz.radar.ClassRadar\");\n    var radarClassResult = radarClz.discoverClass(className);\n    return radarClassResult;\n};\n\nfunction generateFridaMethodOverload(clzVarName, radarMethod) {\n    var overloadJs = \"\";\n    if (!radarMethod.isLocal.value || radarMethod.methodName.value.indexOf(\"-\") > -1) {\n        return overloadJs;\n    }\n    var methodVarName = clzVarName + \"_method_\" + radarMethod.methodName.value + \"_\" + Math.random().toString(36).slice( - 4);\n    overloadJs += \"var \" + methodVarName + \"=\" + clzVarName + \".\" + radarMethod.methodName.value + \".overload(\";\n    if (radarMethod.paramsNum.value > 0) {\n        for (var j = 0; j < radarMethod.paramsNum.value; j++) {\n            overloadJs += \"'\";\n            overloadJs += radarMethod.paramsClasses.value[j];\n            overloadJs += \"'\";\n            if (j < (radarMethod.paramsNum.value - 1)) {\n                overloadJs += \",\";\n            }\n        }\n    }\n    overloadJs += \");\";\n    overloadJs += methodVarName;\n    overloadJs += \".implementation = function(\";\n    var paramsJs = \"\";\n    for (var j = 0; j < radarMethod.paramsNum.value; j++) {\n        paramsJs += radarMethod.parameterNames.value[j];\n        if (j < (radarMethod.paramsNum.value - 1)) {\n            paramsJs += \",\";\n        }\n    }\n    overloadJs += paramsJs;\n    overloadJs += \") {\";\n    var handle = \"this\";\n    if (radarMethod.isStatic.value) {\n        handle = clzVarName;\n    }\n    if (handle == \"this\") {\n        overloadJs += \"var executor = this.hashCode();\";\n    } else {\n        overloadJs += \"var executor = 'Class';\";\n    }\n    overloadJs += \"var beatText = '\" + radarMethod.describe.value + \"';\";\n    overloadJs += \"var beat = newMethodBeat(beatText, executor);\";\n    if (radarMethod.returnClass.value != \"void\") {\n        overloadJs += \"var ret = \";\n    }\n    overloadJs += methodVarName + \".call(\" + handle;\n    if (radarMethod.paramsNum.value > 0) {\n        overloadJs += \",\" + paramsJs + \");\";\n    } else {\n        overloadJs += \");\";\n    }\n\toverloadJs += \"printBeat(beat);\";\n    if (radarMethod.returnClass.value != \"void\") {\n        overloadJs += \"return ret;\";\n    }\n    overloadJs += \"};\";\n    return overloadJs;\n}\n\n//生成构造方法的overload\nfunction generateFridaConstructorMethodOverload(clzVarName, constructorMethod) {\n    var overloadJs = \"\";\n    if (!constructorMethod.isLocal.value) {\n        return overloadJs;\n    }\n    var constructorMethodVarName = clzVarName + \"_init_\" + Math.random().toString(36).slice( - 4);\n    var hookConstructorMethodJs = clzVarName + \".$init.overload(\";\n    if (constructorMethod.paramsNum.value > 0) {\n        for (var j = 0; j < constructorMethod.paramsNum.value; j++) {\n            hookConstructorMethodJs += \"'\";\n            hookConstructorMethodJs += constructorMethod.paramsClasses.value[j];\n            hookConstructorMethodJs += \"'\";\n            if (j < (constructorMethod.paramsNum.value - 1)) {\n                hookConstructorMethodJs += \",\";\n            }\n        }\n    }\n    hookConstructorMethodJs += \");\";\n    overloadJs += \"var \" + constructorMethodVarName + \" = \" + hookConstructorMethodJs;\n    overloadJs += constructorMethodVarName + \".implementation = function(\";\n    var paramsJs = \"\";\n    for (var j = 0; j < constructorMethod.paramsNum.value; j++) {\n        paramsJs += \"v\" + j;\n        if (j < (constructorMethod.paramsNum.value - 1)) {\n            paramsJs += \",\";\n        }\n    }\n    overloadJs += paramsJs;\n    overloadJs += \") {\";\n    overloadJs += \"var executor = this.hashCode();\";\n    overloadJs += \"var beatText = '\" + constructorMethod.describe.value + \"';\";\n    overloadJs += \"var beat = newMethodBeat(beatText, executor);\";\n    overloadJs += \"var returnObj = \";\n    overloadJs += constructorMethodVarName + \".call(this\";\n    if (constructorMethod.paramsNum.value > 0) {\n        overloadJs += \",\" + paramsJs + \");\";\n    } else {\n        overloadJs += \");\";\n    }\n    overloadJs += \"printBeat(beat);\";\n    overloadJs += \"return returnObj;};\";\n    return overloadJs;\n}\n\n//RadarClassResult  string\nfunction generateMethodHookJs(radarClassResult, methodName) {\n    if (radarClassResult.isEnum.value || radarClassResult.isInterface.value) {\n        return \"\";\n    }\n    var hookJs = \"\";\n    var hasHook = false;\n    var clzHookJs = \"\";\n\n    var clzVarName = radarClassResult.className.value.replace(/[\\.$;]/g, \"_\") + \"_clz\";\n    clzHookJs += \"var \" + clzVarName + \" = Java.use('\" + radarClassResult.className.value + \"');\";\n    var methods = radarClassResult.methods.value;\n    var hookMethod = (methodName == \"?\" || methodName == \"*\");\n    var hookConstructorMethod = (methodName == \"_\" || methodName == \"*\");\n    for (var i = 0; i < methods.length; i++) {\n        var radarMethod = methods[i];\n        if (hookMethod || radarMethod.matchName(methodName)) {\n            hasHook = true;\n            clzHookJs += generateFridaMethodOverload(clzVarName, radarMethod);\n        }\n    }\n\n    //是否需要hook构造方法\n    if (hookConstructorMethod) {\n        hasHook = true;\n        var constructorMethods = radarClassResult.constructorMethods.value;\n        for (var i = 0; i < constructorMethods.length; i++) {\n            clzHookJs += generateFridaConstructorMethodOverload(clzVarName, constructorMethods[i]);\n        }\n    }\n\n    if (hasHook) {\n        hookJs += clzHookJs;\n    }\n    return hookJs;\n};\n\nfunction class_exists(className) {\n    var exists = false;\n    try {\n        var clz = Java.use(className);\n        exists = true;\n    } catch(err) {\n        //console.log(err);\n    }\n    return exists;\n};\n\n//可能会超时 为了防止这个发生，可以在函数 setImmediate 中给你的脚本添加一层包装\nrpc.exports = {\n    starthttpserver: function (dex_file, allClz) {\n        var result = \"\";\n        Java.perform(function() {\n            if (dex_file) {\n                var DexClassLoader = Java.use(\"dalvik.system.DexClassLoader\");\n                var ActivityThread = Java.use(\"android.app.ActivityThread\");\n                var app = ActivityThread.currentApplication();\n                var context = app.getApplicationContext();\n                var cacheDir = context.getCodeCacheDir().getAbsolutePath();\n                var parent = context.getClassLoader();\n                var dexPath = \"/data/local/tmp/radar.dex:\" + dex_file;\n                var newLoader = DexClassLoader.$new(\n                    dexPath,\n                    cacheDir,\n                    null,\n                    parent\n                );\n                Java.classFactory.loader = newLoader;\n                var httpServerBoot = Java.use('gz.httpserver.HookerWebServerBoot');\n                var ArrayList = Java.use(\"java.util.ArrayList\");\n                // JS 里分割\n                var arr = allClz.split(\",\");\n                // 创建 ArrayList\n                var clzList = ArrayList.$new();\n                // 填充 ArrayList<String>\n                for (var i = 0; i < arr.length; i++) {\n                    clzList.add(arr[i]);\n                }\n                result = httpServerBoot.scanAndStartHttpServer(clzList);\n            }else{\n                Java.openClassFile(\"/data/local/tmp/radar.dex\").load();\n                var httpServerBoot2 = Java.use('gz.httpserver.HookerWebServerBoot');\n                result = httpServerBoot2.startDefaultHttpServer();\n            }\n        });\n        return result;\n    },\n    loadradardex: function() {\n        Java.perform(function() {\n            if (!class_exists(\"gz.radar.ClassRadar\")) {\n                var context = Java.use(\"android.app.ActivityThread\").currentApplication().getApplicationContext();\n                var packageName = context.getPackageName();\n                Java.openClassFile('/data/local/tmp/radar.dex').load();\n            }\n        });\n    },\n    containsclass: function(className) {\n        var result = false;\n        Java.perform(function() {\n            result = class_exists(className);\n        });\n        return result;\n    },\n    hookjs: function(className, toSpace) {\n        var found = true;\n        var hookJs = \"Java.perform(function() {\\n\";\n        var className = className;\n        var methodName = toSpace;\n        Java.perform(function() {\n            var radarClassResult = discoverClass(className);\n            if (radarClassResult) {\n                hookJs += generateMethodHookJs(radarClassResult, methodName);\n                hookJs += \"});\";\n            } else {\n                found = false;\n                console.error(\"Not found class \" + className);\n            }\n        });\n        if (found) {\n            return hookJs;\n        }\n        return \"\";\n    },\n    write: function(filename, contentAsBase64) {\n        Java.perform(function() {\n            //console.log(contentAsBase64);\n            writeFileAsBase64Content(filename, contentAsBase64);\n        });\n    },\n    checkfile: function(filename, filelength) {\n        var ret = false;\n        Java.perform(function() {\n            ret = checkFile(filename, filelength);\n        });\n        return ret;\n    },\n    activitys: function() {\n        var report = \"\";\n        Java.perform(function() {\n            try {\n                var radarAndroidClz = Java.use(\"gz.radar.Android\");\n                var activityInfos = radarAndroidClz.getActivityInfos();\n                report += (\"Found Activities: \" + activityInfos.length) + \"\\n\";\n                for (var i = 0; i < activityInfos.length; i++) {\n                    try {\n                        report += (\"------------------\" + (i) + \"--------------------\") + \"\\n\";\n                        var activityInfo = activityInfos[i];\n                        report += (\"Activity Title: \" + activityInfo.getTitle()) + \"\\n\";\n                        report += (\"Activity Class: \" + activityInfo.getClazz()) + \"\\n\";\n                        report += (\"Activity SuperClass: \" + activityInfo.getSuperClazz()) + \"\\n\";\n                        report += (\"Activity ImplementInterfaces: \" + activityInfo.getImplementInterfaces()) + \"\\n\";\n                        report += (\"Activity OnTop: \" + activityInfo.isOnTop()) + \"\\n\";\n                        report += (\"Activity Paused: \" + activityInfo.isPaused()) + \"\\n\";\n                        report += (\"Activity Stopped: \" + activityInfo.isStopped()) + \"\\n\";\n                        var androidApkFields = activityInfo.getAndroidApkFields();\n                        report += (\"Activity Fields: \" + androidApkFields.length) + \"\\n\";\n                        for (var j = 0; j < androidApkFields.length; j++) {\n                            report += (\"\\t\" + androidApkFields[j].toLine()) + \"\\n\";\n                        }\n                        var methods = activityInfo.methods();\n                        report += (\"Activity Methods: \" + methods.length) + \"\\n\";\n                        for (var j = 0; j < methods.length; j++) {\n                            report += (\"\\t\" + methods[j]) + \"\\n\";\n                        }\n                    } catch(err) {\n                        console.log(err);\n                    }\n                }\n            } catch(err) {\n                console.log(err);\n            }\n\n        });\n        return report;\n    },\n    services: function() {\n        var report = \"\";\n        Java.perform(function() {\n            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n            var serviceInfos = radarAndroidClz.getServiceInfos();\n            report += \"Found Services: \" + serviceInfos.length + \"\\n\";\n            for (var i = 0; i < serviceInfos.length; i++) {\n                report += (\"------------------\" + (i) + \"--------------------\") + \"\\n\";\n                var serviceInfo = serviceInfos[i];\n                report += (\"Service Class: \" + serviceInfo.getName()) + \"\\n\";\n                report += (\"Service SuperClass: \" + serviceInfo.getSuperClazz()) + \"\\n\";\n                report += (\"Service ImplementInterfaces: \" + serviceInfo.getImplementInterfaces()) + \"\\n\";\n                var androidApkFields = serviceInfo.getAndroidApkFields();\n                report += (\"Service Fields: \" + androidApkFields.length) + \"\\n\";\n                for (var j = 0; j < androidApkFields.length; j++) {\n                    report += (\"\\t\" + androidApkFields[j].toLine()) + \"\\n\";\n                }\n                var methods = serviceInfo.methods();\n                report += (\"Service Methods: \" + methods.length) + \"\\n\";\n                for (var j = 0; j < methods.length; j++) {\n                    report += (\"\\t\" + methods[j]) + \"\\n\";\n                }\n            }\n        });\n        return report;\n    },\n    objectinfo: function(objectId) {\n        var report = \"\";\n\t\tif (class_exists(objectId)) {\n\t\t\t//判断是否是类名\n\t\t\tvar max = 10;\n\t\t\tvar found = [];\n\t\t\tvar class_name = objectId;\n\t\t\tJava.perform(function () {\n\t\t\t\tvar ObjectsStore = Java.use(\"gz.radar.objects.ObjectsStore\");\n\t\t\t    Java.choose(class_name, {\n\t\t\t        onMatch: function (instance) {\n\t\t\t\t\t\tif (found.length >= max) {\n\t\t\t                // 已达上限，直接忽略后续回调\n\t\t\t\t\t\t\tconsole.warn(\"The upper limit has been reached.\");\n\t\t\t                return;\n\t\t\t            }\n\t\t\t\t\t\tfound.push(class_name);\n\t\t\t\t\t\tvar newObjectId = ObjectsStore.storeObject(instance);\n\t\t\t\t\t\tconsole.log(\"Found \" + class_name + \" instance: \" + instance + \" ObjectId: \" + newObjectId);\n\t\t\t        },\n\t\t\t        onComplete: function () {\n\t\t\t            console.log(\"Search complete. Please continue exploring using object with [ObjectId]\");\n\t\t\t        }\n\t\t\t    });\n\t\t\t});\n\t\t}else{\n\t\t\t//不是类名就是object_id\n\t\t\tJava.perform(function() {\n\t            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n\t            var objectInfo = radarAndroidClz.getObjectInfo(objectId);\n\t            if (!objectInfo) {\n\t                report += \"Not Found Any Object.\"\n\t                return;\n\t            }\n\t            report += (\"------------------Object--------------------\") + \"\\n\";\n\t            report += (\"Object Class: \" + objectInfo.getName()) + \"\\n\";\n\t            report += (\"Object SuperClass: \" + objectInfo.getSuperClazz()) + \"\\n\";\n\t            report += (\"Object ImplementInterfaces: \" + objectInfo.getImplementInterfaces()) + \"\\n\";\n\t            var androidApkFields = objectInfo.getAndroidApkFields();\n\t            report += (\"Object Fields: \" + androidApkFields.length) + \"\\n\";\n\t            for (var j = 0; j < androidApkFields.length; j++) {\n\t                report += (\"\\t\" + androidApkFields[j].toLine()) + \"\\n\";\n\t            }\n\t            var methods = objectInfo.methods();\n\t            report += (\"Object Methods: \" + methods.length) + \"\\n\";\n\t            for (var j = 0; j < methods.length; j++) {\n\t                report += (\"\\t\" + methods[j]) + \"\\n\";\n\t            }\n\t        });\n\t\t}\n        return report;\n    },\n    objecttoexplain: function(objectId) {\n        var report = \"\";\n        Java.perform(function() {\n            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n            var explainObjs = radarAndroidClz.object2Explain(objectId);\n            if (explainObjs == null) {\n                report += \"Not Found Any Object.\";\n                return;\n            }\n            if (explainObjs.isEmpty()) {\n                report += \"Cannot Explain the Object \" + objectId+\".\";\n                return;\n            }\n            report += \"Found Objects: \" + explainObjs.size() + \"\\n\";\n            for (var i = 0; i < explainObjs.size(); i++) {\n                var key = explainObjs.getKey(i);\n                var _objectId = explainObjs.getObjectId(i);\n                report += (\"------------------[\" + key + \"]--------------------\") + \"\\n\";\n                report += (\"Object Class: \" + explainObjs.getObjectClass(i)) + \"\\n\";\n                report += (\"Object Id:\" + _objectId) + \"\\n\";\n            }\n        });\n        return report;\n    },\n    viewinfo: function(viewId) {\n        var report = \"\";\n        Java.perform(function() {\n            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n            var viewInfo = radarAndroidClz.getViewInfo(viewId + \"\");\n            if (!viewInfo) {\n                report += \"Not Found Any Views.\"\n                return;\n            }\n            report += (\"------------------View--------------------\") + \"\\n\";\n            report += (\"View Id: \" + viewInfo.getViewId()) + \"\\n\";\n            report += (\"View IdName: \" + viewInfo.getViewIdName()) + \"\\n\";\n            report += (\"View Text: \" + viewInfo.getViewText()) + \"\\n\";\n            report += (\"View Visible: \" + viewInfo.isVisible()) + \"\\n\";\n            report += (\"View Class: \" + viewInfo.getName()) + \"\\n\";\n            report += (\"View SuperClass: \" + viewInfo.getSuperClazz()) + \"\\n\";\n            report += (\"View ImplementInterfaces: \" + viewInfo.getImplementInterfaces()) + \"\\n\";\n            var androidApkFields = viewInfo.getAndroidApkFields();\n            report += (\"View Fields: \" + androidApkFields.length) + \"\\n\";\n            for (var j = 0; j < androidApkFields.length; j++) {\n                report += (\"\\t\" + androidApkFields[j].toLine()) + \"\\n\";\n            }\n            var methods = viewInfo.methods();\n            report += (\"View Methods: \" + methods.length) + \"\\n\";\n            for (var j = 0; j < methods.length; j++) {\n                report += (\"\\t\" + methods[j]) + \"\\n\";\n            }\n        });\n        return report;\n    },\n    appversion: function() {\n        var versionName = \"\";\n        Java.perform(function() {\n            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n            versionName = radarAndroidClz.getVersionName();\n        });\n        return versionName;\n    },\n    mainactivity: function() {\n        var mainactivityName = \"\";\n        Java.perform(function() {\n            var radarAndroidClz = Java.use(\"gz.radar.Android\");\n            mainactivityName = radarAndroidClz.getMainActivity();\n        });\n        return mainactivityName;\n    },\n\tcleanup: function () {\n        // 清理所有拦截器\n        Interceptor.detachAll();\n        // 如果你设置了定时器或 Stalker，也可以清理\n        Stalker.unfollow();\n        // clearInterval(...);\n    }\n};\n\nJava.perform(function() {\n    if (!class_exists(\"gz.radar.ClassRadar\")) {\n        var context = Java.use(\"android.app.ActivityThread\").currentApplication().getApplicationContext();\n        var packageName = context.getPackageName();\n        Java.openClassFile('/data/local/tmp/radar.dex').load();\n    }\n});"
  },
  {
    "path": "js/spoof_signature.js",
    "content": "function spoofSignature() {\n    const originalSignature = \"<ORIGINAL_APK_SIGNATURE>\" //This will be set by patch_apk.py\n    Java.perform(() => {\n        const PackageManager = Java.use(\"android.app.ApplicationPackageManager\");\n        const Signature = Java.use(\"android.content.pm.Signature\");\n        const ActivityThread = Java.use('android.app.ActivityThread');\n        PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(a, b) {\n            const packageInfo = this.getPackageInfo(a, b);\n            const context = ActivityThread.currentApplication().getApplicationContext();\n            const name = context.getPackageName();\n            if (a == name && b == 64) {\n                const signature = Signature.$new(originalSignature);\n                packageInfo.signatures.value = Java.array('android.content.pm.Signature', [signature]);\n            }\n            return packageInfo;\n        }\n    });\n}\n\n"
  },
  {
    "path": "js/ssl_log.js",
    "content": "function startTLSKeyLogger() {\n\n    const SSL_CTX_new_addr = Module.findExportByName('libssl.so', 'SSL_CTX_new');\n    const SSL_CTX_set_keylog_callback_addr = Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback');\n\n\n    const keyLogCallback = new NativeCallback(function (ssl, line) {\n        console.log(new NativePointer(line).readCString());\n    }, 'void', ['pointer', 'pointer']);\n\n    const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback_addr, 'void', ['pointer', 'pointer']);\n    \n    Interceptor.attach(SSL_CTX_new_addr, {\n        onLeave: function(retval) {\n            const ssl = new NativePointer(retval);\n            SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback);\n        }\n    });\n}\n\nstartTLSKeyLogger();"
  },
  {
    "path": "js/text_view.js",
    "content": "function methodInBeat(invokeId, timestamp, methodName, executor) {\n\tvar startTime = timestamp;\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var currentThread = threadClz.currentThread();\n    var stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new());\n    var str = (\"------------startFlag:\" + invokeId + \",objectHash:\"+executor+\",thread(id:\" + currentThread.getId() +\",name:\" + currentThread.getName() + \"),timestamp:\" + startTime+\"---------------\\n\");\n    str += methodName + \"\\n\";\n    str += stackInfo.substring(20);\n    str += (\"------------endFlag:\" + invokeId + \",usedtime:\" + (new Date().getTime() - startTime) +\"---------------\\n\");\n\tconsole.log(str);\n};\n\nJava.perform(function() {\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var textViewClz = Java.use(\"android.widget.TextView\");\n\n    if (textViewClz.setText) {\n        var setTextFunc = textViewClz.setText.overload(\"java.lang.CharSequence\");\n        setTextFunc.implementation = function(v0) {\n            var startTime = new Date().getTime();\n            var clz = this.getClass().getName();\n            var viewId = this.getId();\n            console.log(\"TextViewClz: \" + clz);\n            console.log(\"ViewId: \" + viewId);\n            console.log(\"Text:\" + v0);\n            setTextFunc.call(this, v0);\n            var invokeId = Math.random().toString(36).slice( - 8);\n            var executor = this.hashCode();\n            methodInBeat(invokeId, startTime, \"android.widget.TextView.setText()\", executor);\n        };\n    }\n    if (textViewClz.getText) {\n        var getTextFunc = textViewClz.getText.overload();\n        getTextFunc.implementation = function() {\n            var clz = this.getClass().getName();\n            var viewId = this.getId();\n            var editable = getTextFunc.call(this);\n            console.log(\"TextViewClz: \" + clz);\n            console.log(\"ViewId: \" + viewId);\n            console.log(\"Text: \" + editable.toString());\n            var invokeId = Math.random().toString(36).slice( - 8);\n            var executor = this.hashCode();\n            methodInBeat(invokeId, startTime, \"android.widget.TextView.getText()\", executor);\n            return editable;\n        };\n    }\n});"
  },
  {
    "path": "js/trace_init_proc.js",
    "content": "var hasAlreadyHooked = false;\n\n//init_proc func start addr\nvar startAddr = null;\n\n//init_proc func end addr\nvar endAddr = null;\n\n// replace your so name eg: xxx.so\nvar somodule = \"xxx.so\";\n\nfunction hook_dlopen(){\n    //Android8.0之后加载so通过android_dlopen_ext函数\n    var android_dlopen_ext = Module.findExportByName(null,\"android_dlopen_ext\");\n    console.log(\"addr_android_dlopen_ext\",android_dlopen_ext);\n    Interceptor.attach(android_dlopen_ext,{\n        onEnter:function(args){\n            var pathptr = args[0];\n            if(pathptr!=null && pathptr != undefined){\n                var path = ptr(pathptr).readCString();\n                this.path = path;\n                if(path.indexOf(somodule)!=-1){\n                    console.log(\"android_dlopen_ext:\",path);\n                    trace_init_proc()\n                }\n            }\n        },\n        onLeave:function(retvel){\n            console.log(this.path+\" leave!\");\n        }\n    })\n}\n\nfunction trace_init_proc() {\n    let linker = null;\n    if (Process.pointerSize === 4) {\n        linker = Process.findModuleByName(\"linker\");\n    } else {\n        linker = Process.findModuleByName(\"linker64\");\n    }\n    let call_constructors_addr, get_soname\n    let symbols = linker.enumerateSymbols();\n    for (let index = 0; index < symbols.length; index++) {\n        let symbol = symbols[index];\n        if (symbol.name === \"__dl__ZN6soinfo17call_constructorsEv\") {\n            call_constructors_addr = symbol.address;\n        } else if (symbol.name === \"__dl__ZNK6soinfo10get_sonameEv\") {\n            get_soname = new NativeFunction(symbol.address, \"pointer\", [\"pointer\"]);\n        }\n    }\n    console.log(\"call_constructors_addr:\" + call_constructors_addr);\n    var listener = Interceptor.attach(call_constructors_addr, {\n        onEnter: function (args) {\n            //console.log(\"hooked call_constructors\")\n            var module = Process.findModuleByName(somodule)\n            if (module != null && !hasAlreadyHooked) {\n                hasAlreadyHooked = true;\n                console.log(somodule + \" base address:\", module.base);\n                var targetAddress = module.base.add(startAddr);\n                var absolutStartAddr = targetAddress;\n                var absolutEndAddr = module.base.add(endAddr);\n                console.log(`init_proc func starts at ${absolutStartAddr} ends at ${absolutEndAddr}`);\n                Interceptor.attach(targetAddress, {\n                    onEnter: function(args) {\n                        console.log(`init_proc called at ${targetAddress}`);\n                        // 开启 Stalker 跟踪\n                        Stalker.follow(this.threadId, {\n                            transform: function (iterator) {\n                                var instruction;\n                                while ((instruction = iterator.next()) !== null) {\n                                    var addr = instruction.address;\n                                    if (addr.compare(absolutStartAddr) >= 0 && addr.compare(absolutEndAddr) <= 0) {\n                                        console.log(\"[EXEC] 0x\" + (addr - module.base).toString(16) + \": \" + instruction.toString());\n                                    }\n                                    iterator.keep();\n                                }\n                            }\n                        });\n                    },\n                    onLeave: function(retval) {\n                        console.log(\"Function returned:\", retval);\n                        // 可以修改返回值\n                        // retval.replace(ptr(0x1));\n                    }\n                });\n            }\n\n        },\n    })\n}\nfunction main(){\n    hook_dlopen()\n}\n\nmain()"
  },
  {
    "path": "js/url.js",
    "content": "function tryGetClass(className) {\n    var clz = undefined;\n    try {\n        clz = Java.use(className);\n    } catch(e) {}\n    return clz;\n}\n\nfunction newMethodBeat(text, executor) {\n    var threadClz = Java.use(\"java.lang.Thread\");\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var currentThread = threadClz.currentThread();\n    var beat = new Object();\n    beat.invokeId = Math.random().toString(36).slice( - 8);\n    beat.executor = executor;\n    beat.threadId = currentThread.getId();\n    beat.threadName = currentThread.getName();\n    beat.text = text;\n    beat.startTime = new Date().getTime();\n    beat.stackInfo = androidLogClz.getStackTraceString(exceptionClz.$new()).substring(20);\n    return beat;\n};\n\nfunction printBeat(beat) {\n    var str = (\"------------startFlag:\" + beat.invokeId + \",objectHash:\" + beat.executor + \",thread(id:\" + beat.threadId + \",name:\" + beat.threadName + \"),timestamp:\" + beat.startTime + \"---------------\\n\");\n    str += beat.text + \"\\n\";\n    str += beat.stackInfo;\n    str += (\"------------endFlag:\" + beat.invokeId + \",usedtime:\" + (new Date().getTime() - beat.startTime) + \"---------------\\n\");\n    console.log(str);\n};\n\nvar containRegExps = new Array()\n\nvar notContainRegExps = new Array(RegExp(/\\.jpg/), RegExp(/\\.png/))\n\nfunction check(str) {\n    str = str.toString();\n    if (! (str && str.match)) {\n        return false;\n    }\n    for (var i = 0; i < containRegExps.length; i++) {\n        if (!str.match(containRegExps[i])) {\n            return false;\n        }\n    }\n    for (var i = 0; i < notContainRegExps.length; i++) {\n        if (str.match(notContainRegExps[i])) {\n            return false;\n        }\n    }\n    return true;\n}\n\nJava.perform(function() {\n    var uriParseClz = Java.use('java.net.URI');\n    var uriParseClzConstruct = uriParseClz.$init.overload(\"java.lang.String\");\n    uriParseClzConstruct.implementation = function(url) {\n        var result = uriParseClzConstruct.call(this, url);\n        var executor = this.hashCode();\n        var beatText = \"url:\" + url + \"\\npublic java.net.URI(String)\";\n        var beat = newMethodBeat(beatText, executor);\n        if (check(url)) {\n            printBeat(beat);\n        }\n        return result;\n    };\n\n    // URL\n    var URLClz = Java.use('java.net.URL');\n    var androidLogClz = Java.use(\"android.util.Log\");\n    var exceptionClz = Java.use(\"java.lang.Exception\");\n    var urlConstruct = URLClz.$init.overload(\"java.lang.String\");\n    urlConstruct.implementation = function(url) {\n        var result = urlConstruct.call(this, url);\n        var executor = this.hashCode();\n        var beatText = \"url:\" + url + \"\\npublic java.net.URL(String)\";\n        var beat = newMethodBeat(beatText, executor);\n        if (check(url)) {\n            printBeat(beat);\n        }\n        return result;\n    };\n\n    //ok系统原生支持\n    var sysBuilderClz = tryGetClass('com.android.okhttp.Request$Builder');\n    if (sysBuilderClz) {\n        sysBuilderClz.build.implementation = function() {\n            var okRequestResult = this.build();\n            var httpUrl = okRequestResult.url();\n            var url = httpUrl.toString();\n            var executor = this.hashCode();\n            var beatText = \"url:\" + url + \"\\ncom.android.okhttp.Request.Builder.build()\";\n            var beat = newMethodBeat(beatText, executor);\n            if (check(url)) {\n                printBeat(beat);\n            }\n            return okRequestResult\n        };\n    }\n\n    //ok本地依赖\n    var builderClz = tryGetClass('okhttp3.Request$Builder');\n    if (builderClz) {\n        var buildFunc = builderClz.build.overload();\n        buildFunc.implementation = function() {\n            var okRequestResult = buildFunc.call(this);\n            var httpUrl = okRequestResult.url();\n            var url = httpUrl.toString();\n            var executor = this.hashCode();\n            var beatText = \"url:\" + url + \"\\nokhttp3.Request.Builder.build()\";\n            var beat = newMethodBeat(beatText, executor);\n            if (check(url)) {\n                printBeat(beat);\n            }\n            return okRequestResult\n        };\n    }\n\n    var android_net_Uri_clz = Java.use('android.net.Uri');\n    var android_net_Uri_clz_method_parse_u5rj = android_net_Uri_clz.parse.overload('java.lang.String');\n    android_net_Uri_clz_method_parse_u5rj.implementation = function(url) {\n        var executor = 'Class';\n        var beatText = url + '\\npublic static android.net.Uri android.net.Uri.parse(java.lang.String)';\n        var beat = newMethodBeat(beatText, executor);\n        var ret = android_net_Uri_clz_method_parse_u5rj.call(android_net_Uri_clz, url);\n        if (check(url)) {\n            printBeat(beat);\n        }\n        return ret;\n    };\n});"
  },
  {
    "path": "js/webview_enable_debug.js",
    "content": "function main() {\n    Java.perform(function () {\n        var WebView = Java.use('android.webkit.WebView');\n        var Overloads = [\n            ['android.content.Context'],\n            ['android.content.Context', 'android.util.AttributeSet'],\n            ['android.content.Context', 'android.util.AttributeSet', 'int'],\n            ['android.content.Context', 'android.util.AttributeSet', 'int', 'int'],\n            ['android.content.Context', 'android.util.AttributeSet', 'int', 'boolean'],\n            ['android.content.Context', 'android.util.AttributeSet', 'int', 'java.util.Map', 'boolean'],\n            ['android.content.Context', 'android.util.AttributeSet', 'int', 'int', 'java.util.Map', 'boolean']\n        ];\n\n        Overloads.forEach(function (args) {\n            var overload = WebView.$init.overload.apply(WebView.$init, args);\n            overload.implementation = function () {\n                var result = overload.apply(this, arguments);\n\n                console.log('\\n[+] WebView constructor called with args: ' + JSON.stringify(args));\n                console.log('[+] WebView instance: ' + this);\n\n                try {\n                    var Log = Java.use(\"android.util.Log\");\n                    var Throwable = Java.use(\"java.lang.Throwable\");\n                    console.log('[*] Stack trace:\\n' + Log.getStackTraceString(Throwable.$new()));\n                } catch (e) {\n                    console.log(\"[-] Failed to get stack trace: \" + e);\n                }\n\n                this.setWebContentsDebuggingEnabled(true);\n                console.log('[+] WebContents debugging enabled.\\n');\n\n                return result;\n            };\n        });\n\n        // Hook evaluateJavascript\n        WebView.evaluateJavascript.overload('java.lang.String', 'android.webkit.ValueCallback').implementation = function (script, callback) {\n            console.log('\\n[📡] evaluateJavascript called!');\n            console.log('[📝] Script: \\n' + script);\n\n            try {\n                var Log = Java.use(\"android.util.Log\");\n                var Throwable = Java.use(\"java.lang.Throwable\");\n                console.log('[*] Stack trace:\\n' + Log.getStackTraceString(Throwable.$new()));\n            } catch (e) {\n                console.log(\"[-] Failed to get stack trace: \" + e);\n            }\n\n            return this.evaluateJavascript(script, callback);\n        };\n    });\n}\n\nsetImmediate(main);"
  },
  {
    "path": "mobile-deploy/daemon_app.sh",
    "content": "#!/system/bin/sh\n\nPKG=\"$1\"\n\nif [ -z \"$PKG\" ]; then\n    echo \"Usage: $0 <package_name>\"\n    exit 1\nfi\n\nwhile true\ndo\n    if ! pidof $PKG >/dev/null; then\n        echo \"[$(date)] restart $PKG\"\n\n        monkey -p $PKG -c android.intent.category.LAUNCHER 1\n\n        # 等待APP启动\n        sleep 15\n    else\n        echo \"[$(date)] $PKG alive\"\n        sleep 3\n    fi\ndone"
  },
  {
    "path": "mobile-deploy/redsocks.conf",
    "content": "base {\n    log_debug = on;\n    log_info = on;\n    daemon = on;\n    redirector = iptables;\n}\n\nredsocks {\n    local_ip = 127.0.0.1;\n    local_port = 12345;\n    ip = 10.112.98.171;\n    port = 9998;\n    type = socks5;\n}\n\n"
  },
  {
    "path": "requirements.txt",
    "content": "frida==16.7.19\nfrida-tools==13.7.1\nadbutils==1.2.11\nandroguard==3.3.5\njsbeautifier==1.15.4\ngitpython\nloguru"
  }
]