canyie/pine

GitHub: canyie/pine

基于 ART 运行时的动态 Java 方法钩子框架,可拦截和修改任意 Java 方法调用行为。

Stars: 1454 | Forks: 259

# Pine [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [中文版本](README_cn.md) ## 简介 Pine 是一个基于 ART 运行时动态 Java 方法钩子框架,可以拦截当前进程中的几乎所有 Java 方法调用。 目前支持 Android 4.4(仅 ART) ~ **15 Beta 4**,支持 thumb-2/arm64 架构。 关于其工作原理,请参阅这篇中文[文章](https://canyie.github.io/2020/04/27/dynamic-hooking-framework-on-art/)。 注意:对于使用 arm32/thumb-2 架构的 Android 6.0 设备,参数可能会出错;对于 Android 9.0+,Pine 将禁用隐藏 API 限制策略。 ~~Pine 这个名字代表了一类以喹硫平和氯氮平为代表的抗精神病药物。它也是"Pine Is Not Epic"的首字母缩写。~~ ## 使用说明 ### 基本用法 [![下载](https://img.shields.io/maven-central/v/top.canyie.pine/core.svg)](https://repo1.maven.org/maven2/top/canyie/pine/core/) 在 build.gradle 中添加依赖(如下所示): ``` dependencies { implementation 'top.canyie.pine:core:' } ``` 基本配置: ``` PineConfig.debug = true; // Do we need to print more detailed logs? PineConfig.debuggable = BuildConfig.DEBUG; // Is this process debuggable? ``` 示例 1:监控 Activity 的创建 ``` Pine.hook(Activity.class.getDeclaredMethod("onCreate", Bundle.class), new MethodHook() { @Override public void beforeCall(Pine.CallFrame callFrame) { Log.i(TAG, "Before " + callFrame.thisObject + " onCreate()"); } @Override public void afterCall(Pine.CallFrame callFrame) { Log.i(TAG, "After " + callFrame.thisObject + " onCreate()"); } }); ``` 示例 2:监控所有 Java 线程的创建和销毁 ``` final MethodHook runHook = new MethodHook() { @Override public void beforeCall(Pine.CallFrame callFrame) throws Throwable { Log.i(TAG, "Thread " + callFrame.thisObject + " started..."); } @Override public void afterCall(Pine.CallFrame callFrame) throws Throwable { Log.i(TAG, "Thread " + callFrame.thisObject + " exit..."); } }; Pine.hook(Thread.class.getDeclaredMethod("start"), new MethodHook() { @Override public void beforeCall(Pine.CallFrame callFrame) { Pine.hook(ReflectionHelper.getMethod(callFrame.thisObject.getClass(), "run"), runHook); } }); ``` 示例 3:强制允许任何线程修改 UI: ``` Method checkThread = Class.forName("android.view.ViewRootImpl").getDeclaredMethod("checkThread"); Pine.hook(checkThread, MethodReplacement.DO_NOTHING); ``` ### Xposed 支持 [![下载](https://img.shields.io/maven-central/v/top.canyie.pine/xposed.svg)](https://repo1.maven.org/maven2/top/canyie/pine/xposed/) Pine 支持以 Xposed 风格钩取方法和加载 Xposed 模块。(仅支持 Java 方法钩取。使用不支持的功能(如资源钩取)的模块将无法工作。) ``` implementation 'top.canyie.pine:xposed:' ``` 以 Xposed 风格直接钩取方法: ``` XposedHelpers.findAndHookMethod(TextView.class, "setText", CharSequence.class, TextView.BufferType.class, boolean.class, int.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Log.e(TAG, "Before TextView.setText"); param.args[0] = "hooked"; } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Log.e(TAG, "After TextView.setText"); } }); ``` 或者这样: ``` XposedBridge.hookMethod(target, callback); ``` 并且可以加载 xposed 模块(目前不支持资源钩取): ``` // 1. load modules PineXposed.loadModule(new File(modulePath)); // 2. call all 'IXposedHookLoadPackage' callback PineXposed.onPackageLoad(packageName, processName, appInfo, isFirstApp, classLoader); ``` 注意: 1. 钩子仅在当前进程中生效。如果希望钩子在其他进程中生效,需要先将代码注入到这些进程中。这与我们无关。 2. 使用不支持的功能(如资源钩取或 XSharedPreferences)的模块将无法工作。 ### 增强功能 [![下载](https://img.shields.io/maven-central/v/top.canyie.pine/enhances.svg)](https://repo1.maven.org/maven2/top/canyie/pine/enhances/) 结合 [Dobby](https://github.com/jmpews/Dobby),你可以使用一些增强功能: ``` implementation 'top.canyie.pine:enhances:' ``` - 延迟钩取(也称为待处理钩取)支持,无需立即初始化声明类即可钩取静态方法: ``` PineEnhances.enableDelayHook(); ``` ### ProGuard 如果你使用 Xposed 功能且需要在模块外部调用 Xposed API(例如调用 `PineXposed.loadModule()` 加载外部模块): ``` # 保留 Xposed APIs -keep class de.robv.android.xposed.** { *; } -keep class android.** { *; } ``` ## 已知问题 - 可能与某些设备/系统不兼容。 - 由于 [#11](https://github.com/canyie/pine/issues/11) 的原因,我们强烈建议尽可能钩取并发较少的方法,例如: ``` public static void method() { synchronized (sLock) { methodLocked(); } } private static void methodLocked() { // ... } ``` 在示例中,我们建议钩取 `methodLocked` 而不是 `method`。 - Pine 默认在初始化时禁用隐藏 API 策略。由于 ART 的一个 bug,如果一个线程在另一个线程正在调用列出类成员的 API 时更改隐藏 API 策略,可能会发生越界写入并导致崩溃。我们无法修复系统 bug,唯一的方法是在其他线程启动之前初始化我们的库以避免竞态条件。更多信息,请参阅 tiann/FreeReflection#60。 - 更多问题,请参阅 [issues](
标签:Android, Android安全, API接口, ART运行时, DSL, Java Hook, JS文件枚举, Method Hook, SSH蜜罐, Xposed, 云资产清单, 动态修改, 域名枚举, 方法钩子, 系统调修, 进程注入, 逆向工程