通过前面文章 Android中的Context ,我们可以知道,当我通过Application、Activity或者Service调用getPackageManager()的时候,实质调用的都是ContextImpl类中的getPackageManager方法。
所以我们从ContextImpl类中的getPackageManager方法看起。
ContextImpl.java
private PackageManager mPackageManager;
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
ActivityThread.java
static IPackageManager sPackageManager;
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
ApplicationPackageManager.java
private final IPackageManager mPM;
ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}
首先调用ActivityThread获取到PackageManager远程对象,并且将其封装在ApplicationPackageManager对象中,并且ApplicationPackageManager对象进行返回。
另外,在ActivityThread的sPackageManager静态变量会对PackageManager远程对象进行缓存。
看了上面的过程之后,我们如果想hook PackageManger,我们只需要将ActivityThread的sPackageManager静态变量进行替换,将它替换成我们的代理对象即可。
public class HookPackageManager {
private static final String TAG = "HookPackageManager";
public static void hookPackageManager() {
try {
final Object oldPM = ReflectUtil.getField("android.app.ActivityThread", null, "sPackageManager");
Class iPM = Class.forName("android.content.pm.IPackageManager");
Object newPM = Proxy.newProxyInstance(iPM.getClassLoader(), new Class[] {iPM}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.d(TAG, "method: " + method.getName());
return method.invoke(oldPM, objects);
}
});
ReflectUtil.setField("android.app.ActivityThread", null, "sPackageManager", newPM);
} catch (Exception e) {
e.printStackTrace();
}
}
}
另外,从ContextImpl的getPackageManager()方法可以看到,mPackageManager对ApplicationPackageManager对象进行了缓存,所以,如果在hook之前调用过getPackageManager()方法的话,mPackageManager就不为null,可能导致hook不会生效,所以需要尽可能早的调用该hook方法。
关于ReflectUtil类,就是一个反射工具类,参考文章 Android反射工具类ReflectUtil
为了确保能够hook成功,下面测试用例手动将ContextImpl的mPackageManager对象设置为null。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HookPackageManager.hookPackageManager();
try {
Object object = ReflectUtil.getField("android.content.ContextWrapper", this, "mBase");
ReflectUtil.setField("android.app.ContextImpl", object, "mPackageManager", null);
} catch (Exception e) {
e.printStackTrace();
}
// 随便调用一个PackageManager方法,测试hook是否生效
getPackageManager().isSafeMode();
}
}