为什么 Hook 永远是在 Binder Client端,也就是四大组件这边,而不是在 AMS侧进行 Hook。
AMS 要负责和所有 App 的四大组件进行通信 如果在一个 App 中,在 AMS 层面把剪
切板功能进行了 Hook ,那会导致 Android 系统所有的剪切板功能被 Hook
App 的安装过程,为什么不把 apk 解压缩到本地,这样读取图片就不用每次从 apk包中读取了
每次从 apk 中读取资源,并不是先解压再找图片资源,而是解析 apk 中的 resources.arsc
文件,这个文件中存储着资源的所有信息,包括资源在 apk 中的地址、大小等等,按图索
骥,从这个文件中快速找到相应的资源文件 这是一种很高效的算法
不解压 apk 的好处自然是节省空间
Hook点
1、Activity mlnstrumentation 宇段
缺点:只针对于当前 Activity 生效,因为它只修改了当
Activity 实例的 mlnstrumentation字段
Hook 代码从 MainActivity on Create 方法中转移到自定义的
BaseActivity 中,这样所有继承自 BaseActivity Activity ,它们的 mlnstrumentation就都被 Hook
2、AMN getDefault 方法获取到的对象
3、H 的mCallback 字段
插件的瘦身
用provided代替compile,关键字provided只在编译时会用到相应的jar包,打包成apk后jar包并不会在apk中存在。并且provided只支持jar包,不支持module。
具体操作步骤如下:
1,先创建一个module
2、在module工程的build.gradle中添加一个task。
task makeJar(type: Copy) {
//这行表示如果你已经打过一次包了,再进行打包则把原来的包删掉
delete 'build/libs/test.jar'
//这行表示要打包的文件的路径,根据下面的内容,其实是该路径下的classes.jar
from('build/intermediates/packaged-classes/release/')
//这行表示打包完毕后包的生成路径,也就是生成的包存在哪
into('build/libs/')
//看到这行,如果你对分包有了解的话,你就可以看出来这行它只是将一些类打包了
include('classes.jar')
rename ('classes.jar', 'test.jar')
}
makeJar.dependsOn(build)
3、在窗口执行 gradlew makeJar命令,gradlew 后面的这个是根据task的名字操作的。
jar包与aar包的区别:
jar: 只包含了class文件与清单文件 ,不包含资源文件,如图片等所有res中的文件。
aar: 包含jar包和资源文件,如图片等所有res中的文件。
Hook Activity 的几种姿势
Hook 的选择点:静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
Hook 过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。
选择合适的代理方式,如果是接口可以用动态代理。
偷梁换柱——用代理对象替换原始对象。
hook activity
1、Activity startActivity
2、Context startActivity
这两种的区别是 一个是hook Activity的Instrumentation,一个是hook ActivityThread的Instrumentation。
public static void hookInstrumentation(Context context) {
try {
Class<?> contextImplClazz = Class.forName("android.app.ContextImpl");
//final @NonNull ActivityThread mMainThread;
Field mMainThreadField = contextImplClazz.getDeclaredField("mMainThread");
mMainThreadField.setAccessible(true);
//2.ActivityThread Object
Object activityThreadObj = mMainThreadField.get(context);
//3.mInstrumentation Object
Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
//Instrumentation mInstrumentation;
Field mInstrumentationField = activityThreadClazz.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentationObj = (Instrumentation) mInstrumentationField.get(activityThreadObj);
//4.reset set value
mInstrumentationField.set(activityThreadObj, new InstrumentationProxy(mInstrumentationObj, context.getPackageManager(), StubAppCompatActivity.class));
}catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static class InstrumentationProxy extends Instrumentation {
private final Instrumentation mInstrumentation;
private final PackageManager mPackageManager;
private final Class<?> mStubActivityClass;
public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager, Class<?> stubActivityClassName) {
mInstrumentation = instrumentation;
mPackageManager = packageManager;
mStubActivityClass = stubActivityClassName;
}
/**
* android16-android29
* Instrumentation的execStartActivity方法激活Activity生命周期
* 使用占坑的Activity来通过AMS的验证.
*/
@Keep
@SuppressWarnings("unused")
public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
List<ResolveInfo> resolveInfoList = null;
try {
int flags = 0;
if (Build.VERSION.SDK_INT >= 23) {
flags = PackageManager.MATCH_ALL;
}
resolveInfoList = mPackageManager.queryIntentActivities(intent, flags);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Intent finalIntent = intent;
if (resolveInfoList == null || resolveInfoList.size() == 0) {
//目标Activity没有在AndroidManifest.xml中注册的话,将目标Activity的ClassName保存到桩Intent中.
finalIntent = new Intent(who, mStubActivityClass);
//public class Intent implements Parcelable;
//Intent类已经实现了Parcelable接口
finalIntent.putExtra(TARGET_INTENT_CLASS, intent);
}
try {
//通过反射调用execStartActivity方法,这样就可以用桩Activity通过AMS的验证.
Method execStartActivityMethod = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
execStartActivityMethod.setAccessible(true);
return (Instrumentation.ActivityResult) execStartActivityMethod.invoke(mInstrumentation, who, contextThread, token, target, finalIntent, requestCode, options);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* just for android-15
* Instrumentation#execStartActivity()方法参数和其他版本不同, 需要单独适配
* Instrumentation的execStartActivity方法激活Activity生命周期
* 使用占坑的Activity来通过AMS的验证.
* http://androidxref.com/4.0.3_r1/xref/frameworks/base/core/java/android/app/Instrumentation.java
*/
@Keep
@SuppressLint("WrongConstant")
@SuppressWarnings("unused")
public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
if (Build.VERSION.SDK_INT != 15) return null;
List<ResolveInfo> resolveInfoList = null;
try {
int flags = 0;
resolveInfoList = mPackageManager.queryIntentActivities(intent, flags);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Intent finalIntent = intent;
if (resolveInfoList == null || resolveInfoList.size() == 0) {
//目标Activity没有在AndroidManifest.xml中注册的话,将目标Activity的ClassName保存到桩Intent中.
finalIntent = new Intent(who, mStubActivityClass);
//public class Intent implements Parcelable;
//Intent类已经实现了Parcelable接口
finalIntent.putExtra(TARGET_INTENT_CLASS, intent);
}
try {
//just for android-15
//通过反射调用execStartActivity方法,这样就可以用桩Activity通过AMS的验证.
Method execStartActivityMethod = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class);
execStartActivityMethod.setAccessible(true);
return (Instrumentation.ActivityResult) execStartActivityMethod.invoke(mInstrumentation, who, contextThread, token, target, finalIntent, requestCode);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* Instrumentation的newActivity方法,用类加载器来创建Activity实例
* 还原目标Activity.
*/
@Keep
@Override
public Activity newActivity(ClassLoader classLoader, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Intent pluginIntent = intent.getParcelableExtra(TARGET_INTENT_CLASS);
boolean pluginIntentClassNameExist = pluginIntent != null && !TextUtils.isEmpty(pluginIntent.getComponent().getClassName());
//1.className
String finalClassName = pluginIntentClassNameExist ? pluginIntent.getComponent().getClassName() : className;
//2.intent
Intent finalIntent = pluginIntentClassNameExist ? pluginIntent : intent;
//3.classLoader
File pluginDexFile = MyApplication.getInstance().getFileStreamPath(PluginApkNameVersion.PLUGIN_ACTIVITY_APK);
ClassLoader finalClassLoader = pluginIntentClassNameExist ? CustomClassLoader.getPluginClassLoader(pluginDexFile, "com.example.pluginactivity") : classLoader;
if (Build.VERSION.SDK_INT >= 28) {
return mInstrumentation.newActivity(finalClassLoader, finalClassName, finalIntent);
}
return super.newActivity(finalClassLoader, finalClassName, finalIntent);
}
}
Hook AMS
上面 hook activity 的两种方法其实都有一定缺陷,比如,第一种方法,只能 hook 住通过 Activity startActivity 的 activity。第二种方法,只能 hook 住通过 getApplicationContext().startActivity 启动的 activity。那有没有一种方法能 hook 上述两种的,其实是有的,那就是 hook AMS。
步骤:
在 AMS 通过 intent 校验 activity 是否注册的时候,用已经在 AndroidManifet 注册的 Activity 欺骗 AMS,绕过 原有 activity 的校验,并将原有的 intent 信息储存起来
在 AMS 校验完毕的时候,通过 binder 告知我们的应用启动相应 activity 的时候,我们将 intent 的信息取出来,还原。
public static void hookStartActivity(Context context, Class<?> subActivityClass) {
try {
if (Build.VERSION.SDK_INT >= 29){
//1.获取ActivityTaskManager的Class对象
//package android.app;
//public class ActivityTaskManager
@SuppressLint("PrivateApi") Class<?> activityTaskManagerClazz = Class.forName("android.app.ActivityTaskManager");
//2.获取ActivityTaskManager的私有静态成员变量IActivityTaskManagerSingleton
// private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton
Field iActivityTaskManagerSingletonField = activityTaskManagerClazz.getDeclaredField("IActivityTaskManagerSingleton");
iActivityTaskManagerSingletonField.setAccessible(true);
//获取实例
Object IActivityTaskManagerSingletonObj = iActivityTaskManagerSingletonField.get(null);
handleIActivityTaskManager(context,subActivityClass,IActivityTaskManagerSingletonObj);
}else if (Build.VERSION.SDK_INT > 26){
//1.获取ActivityManager的Class对象
//package android.app
//public class ActivityManager
Class<?> activityManagerClazz = Class.forName("android.app.ActivityManager");
//2.获取ActivityManager的私有静态属性IActivityManagerSingleton
//private static final Singleton<IActivityManager> IActivityManagerSingleton
Field iActivityManagerSingletonField = activityManagerClazz.getDeclaredField("IActivityManagerSingleton");
//3.取消Java的权限检查
iActivityManagerSingletonField.setAccessible(true);
//4.获取IActivityManagerSingleton的实例对象
//private static final Singleton<IActivityManager> IActivityManagerSingleton
//所有静态对象的反射可以通过传null获取,如果是非静态必须传实例
handleIActivityManager(context, subActivityClass, iActivityManagerSingletonField.get(null));
}else {
//1.获取ActivityManagerNative的Class对象
//package android.app
//public abstract class ActivityManagerNative
@SuppressLint("PrivateApi") Class<?> activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative");
//2.获取 ActivityManagerNative的 私有属性gDefault
// private static final Singleton<IActivityManager> gDefault
Field singletonField = activityManagerNativeClazz.getDeclaredField("gDefault");
//3.对私有属性gDefault,解除私有限定
singletonField.setAccessible(true);
//4.获得gDefaultField中对应的属性值(被static修饰了),既得到Singleton<IActivityManager>对象的实例
//所有静态对象的反射可以通过传null获取
//private static final Singleton<IActivityManager> gDefault
handleIActivityManager(context, subActivityClass, singletonField.get(null));
}
}catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static void handleIActivityManager(Context context, Class<?> subActivityClass, Object IActivityManagerSingletonObj) {
try {
//5.获取private static final Singleton<IActivityManager> IActivityManagerSingleton对象中的属性private T mInstance的值
//既,为了获取一个IActivityManager的实例对象
//private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>(){...}
//6.获取Singleton类对象
//package android.util
//public abstract class Singleton<T>
Class<?> singletonClazz = Class.forName("android.util.Singleton");
//7.获取mInstance属性
//private T mInstance;
Field mInstanceField = singletonClazz.getDeclaredField("mInstance");
//8.取消Java的权限检查
mInstanceField.setAccessible(true);
//9.获取mInstance属性的值,既IActivityManager的实例
//从private static final Singleton<IActivityManager> IActivityManagerSingleton实例对象中获取mInstance属性对应的值,既IActivityManager
Object iActivityManager = mInstanceField.get(IActivityManagerSingletonObj);
//10.获取IActivityManager接口的类对象
//package android.app
//public interface IActivityManager
Class<?> iActivityManagerClazz = Class.forName("android.app.IActivityManager");
if (mIActivityInvocationHandlerL == null) {
mIActivityInvocationHandlerL = new IActivityInvocationHandler(iActivityManager, context, subActivityClass);
} else {
mIActivityInvocationHandlerL.updateStubActivity(subActivityClass);
}
//11.创建一个IActivityManager接口的代理对象
Object iActivityManagerProxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{iActivityManagerClazz},
mIActivityInvocationHandlerL
);
//11.重新赋值
//给mInstance属性,赋新值
//给Singleton<IActivityManager> IActivityManagerSingleton实例对象的属性private T mInstance赋新值
mInstanceField.set(IActivityManagerSingletonObj, iActivityManagerProxy);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static class HandlerCallback implements Handler.Callback{
private final Context context;
private final Class<?> subActivityClazz;
private final boolean isAppCompat;
public HandlerCallback(Context context, Class<?> subActivityClazz, boolean isAppCompat) {
this.context = context;
this.subActivityClazz = subActivityClazz;
this.isAppCompat = isAppCompat;
}
@Override
public boolean handleMessage(@NonNull Message msg) {
handleLaunchActivity(msg, context, subActivityClazz, isAppCompat);
return false;
}
private void handleLaunchActivity(Message msg, Context context, Class<?> subActivityClazz, boolean isAppCompat) {
int LAUNCH_ACTIVITY = 100;
try {
//1.获取ActivityThread的内部类H的Class对象
//package android.app
//public final class ActivityThread{
// private class H extends Handler {}
//}
Class<?> hClazz = Class.forName("android.app.ActivityThread$H");
//2.获取LAUNCH_ACTIVITY属性的Field
// public static final int LAUNCH_ACTIVITY = 100;
Field launch_activity_field = hClazz.getField("LAUNCH_ACTIVITY");
//3.获取LAUNCH_ACTIVITY的值
Object object = launch_activity_field.get(null);
if (object instanceof Integer) {
LAUNCH_ACTIVITY = (int) object;
}
} catch (Exception e) {
e.printStackTrace();
}
if (msg.what != LAUNCH_ACTIVITY) return;
// private class H extends Handler {
// public void handleMessage(Message msg) {
// switch (msg.what) {
// case LAUNCH_ACTIVITY: {
// final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
// r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
// handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
// break;
// }
// }
// }
//1.从msg中获取ActivityClientRecord对象
//android.app.ActivityThread$ActivityClientRecord
//static final class ActivityClientRecord {}
Object activityClientRecordObj = msg.obj;
try {
//2.获取ActivityClientRecord的intent属性
// Intent intent;
Field safeIntentField = activityClientRecordObj.getClass().getDeclaredField("intent");
safeIntentField.setAccessible(true);
//3.获取ActivityClientRecord的intent属性的值,既安全的Intent
Intent safeIntent = (Intent) safeIntentField.get(activityClientRecordObj);
if (safeIntent == null) return;
//4.获取原始的Intent
Intent originIntent = safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
if (originIntent == null) return;
//5.将安全的Intent,替换为原始的Intent,以启动我们要启动的未注册的Activity
safeIntent.setComponent(originIntent.getComponent());
//6.处理启动的Activity为AppCompatActivity类或者子类的情况
if (!isAppCompat) {
return;
}
hookPackageManager(context, subActivityClazz);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static class HandlerCallbackP implements Handler.Callback{
private final Context context;
private final Class<?> subActivityClazz;
private final boolean isAppCompat;
public HandlerCallbackP(Context context, Class<?> subActivityClazz, boolean isAppCompat) {
this.context = context;
this.subActivityClazz = subActivityClazz;
this.isAppCompat = isAppCompat;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public boolean handleMessage(@NonNull Message msg) {
//android.app.ActivityThread$H.EXECUTE_TRANSACTION = 159
//android 9.0反射,Accessing hidden field Landroid/app/ActivityThread$H;->EXECUTE_TRANSACTION:I (dark greylist, reflection)
//android9.0 深灰名单(dark greylist)则debug版本在会弹出dialog提示框,在release版本会有Toast提示,均提示为"Detected problems with API compatibility"
if (msg.what == 159){
handleActivity(msg);
}
return false;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void handleActivity(Message msg) {
/**
* final ClientTransaction transaction = (ClientTransaction) msg.obj;
* mTransactionExecutor.execute(transaction);
*
* 在API 30 startActivity在H里面进行了改变
* ClientTransaction-->ClientTransaction中的List<ClientTransactionItem> mActivityCallbacks-->集合中的第一个值LaunchActivityItem-->LaunchActivityItem的mIntent
*/
try {
//1.获取ClientTransaction对象
Object clientTransactionObj = msg.obj;
if (clientTransactionObj == null){
return;
}
//2.获取ClientTransaction类中属性mActivityCallbacks的Field
//private List<ClientTransactionItem> mActivityCallbacks;
Field mActivityCallbackField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbackField.setAccessible(true);
List<?> mActivityCallbacks = (List<?>) mActivityCallbackField.get(clientTransactionObj);
if (mActivityCallbacks == null || mActivityCallbacks.size() <=0){
return;
}
if (mActivityCallbacks.get(0) == null){
return;
}
//5.ClientTransactionItem的Class对象
//package android.app.servertransaction;
//public class LaunchActivityItem extends ClientTransactionItem
@SuppressLint("PrivateApi") Class<?> launchActivityItemClazz = Class.forName("android.app.servertransaction.LaunchActivityItem");
//6.判断集合中第一个元素的值是LaunchActivityItem类型的
if (!launchActivityItemClazz.isInstance(mActivityCallbacks.get(0))){
return;
}
Object launchActivityItem = mActivityCallbacks.get(0);
//8.ClientTransactionItem的mIntent属性的mIntent的Field
//private Intent mIntent;
Field mIntentField = launchActivityItemClazz.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
//10.获取mIntent属性的值,既桩Intent(安全的Intent)
//从LaunchActivityItem中获取属性mIntent的值
Intent safeIntent = (Intent) mIntentField.get(launchActivityItem);
if (safeIntent == null){
return;
}
Intent originIntent = safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
if (originIntent == null){
return;
}
safeIntent.setComponent(originIntent.getComponent());
if (!isAppCompat){
return;
}
hookPackageManager(context,subActivityClazz);
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
}
插件资源与宿主资源id冲突
1、修改 Android 打包流程中使用到的 aapt ,为插件的资源 id 指定 Ox71
的前缀, 就可避免与宿主资 id冲突
2、,仍然是修改插件的资源 id 前缀为 Ox71 ,但在 Android 打包生成 resources.
arsc 件之 ,对这个 resources arsc 文件进行修改
3、进入到哪个插件,就为这个插件 AssetManager Resources
象, 使用这 新对象 资源,就只能是插件中 的资 永远不 和宿主冲突
插件的混淆
把 Pluginl 拆成两个包, Pluginl的代码都放在主 dex 中,而其他代码都放在 classes2.dex 中,包括 MyPluginLibrary后,我们用一个空的 classes2.dex 文件,替换 Plugin I.a pk 中的 asses2.dex 后,让Pluginl HostApp 使用相同的混淆规则 我们可以混 Pluginl Pluginl 的混淆规则文件放在 HostApp 中,那么 HostApp, MyPluginLibrary Pluginl 使用相同的混淆规则。
Service的插件化
原理:先在宿主app中创建一个货真价实的Service,然后在这个Service的onStart和onDestory方法中去拦截分发
所有代码请参考 https://github.com/fengyuehan/Test/tree/master/androidplugin
网友评论