Pine is a dynamic java method hook framework on ART runtime, which can intercept almost all java method calls in the current process.
Currently it supports Android 4.4(ART only) ~ 14 with thumb-2/arm64 architecture.
About its working principle, you can refer to this Chinese article.
Note: For Android 6.0 devices with arm32/thumb-2 architectures, the arguments may be wrong; and for Android 9.0+, pine will disable the hidden api restriction policy.
The name, Pine, represents a class of antipsychotic drugs represented by Quetiapine and Clozapine. It is also an acronym for "Pine Is Not Epic".
Add dependencies in build.gradle (like this):
dependencies {
implementation 'top.canyie.pine:core:<version>'
}
Basic configuration:
PineConfig.debug = true; // Do we need to print more detailed logs?
PineConfig.debuggable = BuildConfig.DEBUG; // Is this process debuggable?
Example 1: monitor the creation of activities
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()");
}
});
Example 2: monitor the creation and destroy of all java threads
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);
}
});
Example 3: force allow any threads to modify ui:
Method checkThread = Class.forName("android.view.ViewRootImpl").getDeclaredMethod("checkThread");
Pine.hook(checkThread, MethodReplacement.DO_NOTHING);
Pine supports hooking methods in Xposed-style and loading Xposed modules. (Only java method hooking is supported. Modules using unsupported features like Resource-hooking won't work.)
implementation 'top.canyie.pine:xposed:<version>'
Directly hook methods in Xposed-style:
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");
}
});
or like this:
XposedBridge.hookMethod(target, callback);
and you can load xposed modules (resources hook is not supported now):
// 1. load modules
PineXposed.loadModule(new File(modulePath));
// 2. call all 'IXposedHookLoadPackage' callback
PineXposed.onPackageLoad(packageName, processName, appInfo, isFirstApp, classLoader);
With Dobby, you can use some enhanced features:
implementation 'top.canyie.pine:enhances:<version>'
- Delay hook (aka pending hook) support, hooking static methods without initializing its declaring class immediately:
PineEnhances.enableDelayHook();
If you are using enhanced features:
# Pine Enhances
-keep class top.canyie.pine.enhances.PineEnhances {
private static void onClassInit(long);
}
If you use Xposed features and Xposed APIs need to be called outside your module (e.g. you call PineXposed.loadModule()
to load external modules):
# Keep Xposed APIs
-keep class de.robv.android.xposed.** { *; }
-keep class android.** { *; }
-
May not be compatible with some devices/systems.
-
Due to #11, we recommend hooking methods with less concurrency as much as possible, for example:
public static void method() {
synchronized (sLock) {
methodLocked();
}
}
private static void methodLocked() {
// ...
}
In the example, we recommend you to hook methodLocked
instead of method
.
-
Pine will disable hidden api policy on initialization by default. Due to an ART bug, if a thread changes hidden api policy while another thread is calling a API that lists members of a class, a out-of-bounds write may occur and causes crashes. We have no way to fix system bugs, so the only way is, initialize our library before other threads is started to avoid the race condition. For more info, see tiann/FreeReflection#60.
-
For more, see issues.
QQ Group:949888394 Telegram Group: @DreamlandFramework
AndroidELF Copyright (c) Swift Gan
Licensed under the Anti 996 License, Version 1.0 (the "License");
you may not use this "Pine" project except in compliance with the License.
You may obtain a copy of the License at