前言
本章继续讲解 AMS 相关的知识点,如何 Hook Activity 的启动流程;
attach
我们这样直接从 attach 入口讲起
java
private void attach(boolean system, long startSeq) {
// 省略部分代码
// AMS
final IActivityManager mgr = ActivityManager.getService();
try {
// 这里直接调用到了 AMS 的 attachApplication 方法;
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
attach 中直接调用到了 AMS 的 attachApplication 方法,我们进入这个方法看下:
arduino
public final void attachApplication(IApplicationThread thread, long startSeq) {
if (thread == null) {
throw new SecurityException("Invalid application interface");
}
synchronized (this) {
// 获取调用端的 pid
int callingPid = Binder.getCallingPid();
// 获取调用端的 uid
final int callingUid = Binder.getCallingUid();
// 把 pid/uid 设置成 AMS 的
final long origId = Binder.clearCallingIdentity();
//
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
// 恢复为 app 的
Binder.restoreCallingIdentity(origId);
}
}
这里有一个比较关键的点就是:把 binder 的 id 设置成 AMS 的,这样做是为了权限验证;Binder.clearCallingIdentity() 最终调用到 IPCThreadState.cpp 中的同名方法,我们进入这个 cpp 文件的同名方法看下:
arduino
int64_t IPCThreadState::clearCallingIdentity() {
int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
clearCaller();
return token;
}
这里调用了 clearCaller 函数,我们进入这个函数看下:
ini
// 清空远程调用端的uid和pid,用当前本地进程的uid和pid替代
void IPCThreadState::clearCaller() {
mCallingPid = getpid();
mCallingUid = getuid();
}
restoreCallingIdentity 也是调用的同名方法;
arduino
// 从token中解析出pid和uid,并赋值给相应变量,该方法正好是clearCallingIdentity的相反过程
void IPCThreadState::restoreCallingIdentity(int64_t token) {
mCallingUid = (int)(token>>32);
mCallingPid = (int)token;
}
AndroidManifest.xml 中的 Activity 是在哪里注册的?
我们在启动一个 Activity 的时候,如果没有在 AndroidManifest 中进行注册的话,就会抛出一段异常,那么 AndroidManifest.xml 中的 Activity 是在哪里注册的呢?
我们直接进入启动 Activity 的代码中看下:
scss
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
// 省略部分代码
// 调用 AMS 的 startActivity 方法并获取一个放回结果,然后检测这个结果
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
}
这里调用 AMS 的 startActivity 方法并获取一个返回结果,然后检测这个结果;
typescript
public static void checkStartActivityResult(int res, Object intent) {
if (!ActivityManager.isStartResultFatalError(res)) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
}
}
可以看到,在这个检测结果方法中,抛出了 Activity 未注册的异常,到这里,我们的报错点找到了;
我们接着来看下启动流程中是在哪里返回的 result 的,根据上一章节的启动流程介绍,我们很容易找到在 ActivityStarter 的 executeRequest 方法中返回了一个 result;
ini
private int executeRequest(Request request) {
// 省略部分代码
// 可以看到 error 的初始值为 SUCCESS,默认初始化成功,我们接着往下看 err 的赋值的地方
int err = ActivityManager.START_SUCCESS;
// 权限拒绝的时候返回的 err 信息;
if (caller != null) {
callerApp = mService.getProcessController(caller);
if (callerApp != null) {
callingPid = callerApp.getPid();
callingUid = callerApp.mInfo.uid;
} else {
// 权限拒绝返回 PERMISSION_DENIED
err = ActivityManager.START_PERMISSION_DENIED;
}
}
// 从 Intent 中无法找到相应的 Component
if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
// 从 Intent 中无法找到相应的 ActivityInfo
if (err == ActivityManager.START_SUCCESS && aInfo == null) {
// We couldn't find the specific class specified in the Intent.
// Also the end of the line.
err = ActivityManager.START_CLASS_NOT_FOUND;
}
}
到这里的时候,我们就找到前面说的异常抛出的原因,是因为 aInfo == null,那么这个 aInfo 是怎么赋值的呢?它又是因为什么原因是 null 呢?我们接着往回看;
vbscript
private int executeRequest(Request request) {
// 是从 request 的 activityInfo 获取的,这个 request 是传递进来的,我们接着往上一层看
ActivityInfo aInfo = request.activityInfo;
}
是从 request 的 activityInfo 获取的,这个 request 是传递进来的,我们接着往上一层看;
csharp
int execute() {
// 解析 activityInfo
if (mRequest.activityInfo == null) {
mRequest.resolveActivity(mSupervisor);
}
}
可以看到在 execute 中会执行 activityInfo 的解析逻辑,我们进入这个方法看下它在什么情况下会返回 null;
java
void resolveActivity(ActivityTaskSupervisor supervisor) {
// 如果 rInfo 不为 null 则获取 rInfo 的 activityInfo 赋值给 aInfo 并返回;
final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
}
如果 rInfo 不为 null 则获取 rInfo 的 activityInfo 赋值给 aInfo 并返回;那么这个 rInfo 的 activityInfo 是什么情况在为空的呢?这个 rInfo 又是在哪里赋值并传递进来的呢?我们接着返回看;
javascript
void resolveActivity(ActivityTaskSupervisor supervisor) {
// 省略部分代码
// 在这里获取 rInfo
resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
0 /* matchFlags */,
computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
}
通过 resolveIntent 获取 rInfo,我们进入这个 resolveIntent 方法看下;
arduino
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
// 省略部分代码
// 返回 ResolveInfo
try {
return mService.getPackageManagerInternalLocked().resolveIntent(
intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
filterCallingUid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
这里又回到了 AMS 中的 resolveIntent 方法,我们进入这个方法看下;
ini
PackageManagerInternal getPackageManagerInternalLocked() {
if (mPmInternal == null) {
mPmInternal = LocalServices.getService(PackageManagerInternal.class);
}
return mPmInternal;
}
这里是先获取 PackageManagerInternal 然后调用其 resolveIntent 方法,这个 PackageManagerInternal 是一个抽象类,resolveIntent 是一个抽象方法,我们需要进入 PackageManagerInternal 的实现类看下, 它的具体实现就是 PackageManagerService,我们进入它的 resolveIntent 方法看下,它最终调用的是 resolveIntentInternal;
java
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
@PrivateResolveFlags int privateResolveFlags, int userId, boolean resolveForStart,
int filterCallingUid) {
// 省略部分代码
// 获取 ResolveInfo 并返回
final ResolveInfo bestChoice =
chooseBestActivity(
intent, resolvedType, flags, privateResolveFlags, query, userId,
queryMayBeFiltered);
}
通过调用 chooseBestActivity 获取 ResolveInfo 并返回,我们进入这个方法看下;
java
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
boolean queryMayBeFiltered) {
// 省略部分的代码
// 获取 ResolveInfo,如果不为空,直接返回
ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
if (ri != null) {
return ri;
}
}
我们接着进入这个 findPreferredActivityNotLocked 方法看下;
arduino
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
// 省略部分代码
// 这里主要是获取 ActivityInfo
final ActivityInfo ai = getActivityInfo(
pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
| MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
userId);
}
findPreferredActivityNotLocked 主要是为了获取 ActivityInfo 并将 ActivityInfo 赋值给 ResolveInfo 的 resolveInfo;
arduino
public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
arduino
public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
// 省略部分代码
return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
}
java
protected ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
int filterCallingUid, int userId) {
ParsedActivity a = mComponentResolver.getActivity(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
a, flags, ps.readUserState(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
return PackageParser.generateActivityInfo(
mResolveActivity, flags, new PackageUserState(), userId);
}
return null;
}
可以看到往下的逻辑,就是解析 AndroidManifest.xml 文件来获取对应的目标 Activity 信息并返回,如果解析不到,则抛出相应的异常,关于解析 AndroidManifest.xml 会在 PackageManagerService 的讲解中重点讲解;
Hook Activity
Hook Activity 本质就是绕过 AMS 的检查,可以启动我们并没有在 AndroidManifest 中声明的 Activity;
本章只是讲解下 Hook 的原理,具体实战可以关注我的后续文章,后面在插件化实战的时候,会具体实现 Hook AMS;
在 startActivity 的时候,会和 AMS 通信,经过 AMS 的验证之后,返回 app 所在进程,通过 Instrumention 创建目标 Activity,AMS 控制 Instrumention 执行 Activity 的生命周期;
也就说,我们不能传递真正的 Activity 给 AMS,我们需要传递一个假的(也就是在 AndroidManifest 中注册的 Activity),当 AMS 验证通过并返回到 app 的时候,我们需要再替换回目标 Activity 来完成,目标 Activity 的启动;
我们需要寻找至少两个 Hook 点;
- startActivity 的时候,将目标 Activity 替换成假的 Activity;
- AMS 验证通过之后,再替换回去;
整体的启动流程如上图,我们需要在这几个流程中找到比较容易 hook 的点;
找 hook 点,我们一般找 public static 的,这样比较容易反射修改;
从图中以及结合我们前面讲的启动流程,
- Instrumentation 是一个 hook 点(滴滴 VirtualAPK 开源框架方案);
- IActivityManager 是一个 hook 点(360 DroidPlugin 开源框架方案);
这里我贴了一下 360 的具体实现方案(仅 Hook IActivityManager 的地方)
ini
public static void hookIActivityTaskManager(){
try{
Field singletonField = null;
// 这里要适配不同的系统版本,后面插件化的时候会具体讲解
Class<?> actvityManager = Class.forName("android.app.ActivityTaskManager");
singletonField = actvityManager.getDeclaredField("IActivityTaskManagerSingleton");
singletonField.setAccessible(true);
Object singleton = singletonField.get(null);
// 拿IActivityManager对象
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// 原始的IActivityTaskManager
final Object IActivityTaskManager = mInstanceField.get(singleton);
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
, new Class[]{Class.forName("android.app.IActivityTaskManager")}
, new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
Intent raw = null;
int index = -1;
if ("startActivity".equals(method.getName())) {
for (int i = 0; i < args.length; i++) {
if(args[i] instanceof Intent){
raw = (Intent)args[i];
index = i;
}
}
// 代替的Intent
Intent newIntent = new Intent();
newIntent.setComponent(new ComponentName("com.example.hook", StubActivity.class.getName()));
newIntent.putExtra(EXTRA_TARGET_INTENT,raw);
args[index] = newIntent;
}
return method.invoke(IActivityTaskManager, args);
}
});
mInstanceField.set(singleton, proxy);
} catch (Exception e){
e.printStackTrace();
}
}
AMS 验证之前的 Hook,接下来是 AMS 验证之后的 Hook;
java
public static void hookHandler() {
try {
// 这里要适配不同的系统版本,后面插件化的时候会具体讲解
Class<?> atClass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField = atClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadField.get(null);
// ActivityThread 一个app进程 只有一个,获取它的mH
Field mHField = atClass.getDeclaredField("mH");
mHField.setAccessible(true);
final Handler mH = (Handler) mHField.get(sCurrentActivityThread);
//获取mCallback
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mH, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 100: {
}
break;
case 159: {
Object obj = msg.obj;
Log.i(TAG, "handleMessage: obj=" + obj);
try {
Field mActivityCallbacksField = obj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacks = (List) mActivityCallbacksField.get(obj);
if (mActivityCallbacks.size() > 0) {
Log.i(TAG, "handleMessage: size= " + mActivityCallbacks.size());
String className = "android.app.servertransaction.LaunchActivityItem";
if (mActivityCallbacks.get(0).getClass().getCanonicalName().equals(className)) {
Object object = mActivityCallbacks.get(0);
Field intentField = object.getClass().getDeclaredField("mIntent");
intentField.setAccessible(true);
Intent intent = (Intent) intentField.get(object);
Intent targetIntent = intent.getParcelableExtra(EXTRA_TARGET_INTENT);
intent.setComponent(targetIntent.getComponent());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
break;
}
mH.handleMessage(msg);
return true;
}
});
} catch (Exception e) {
Log.e(TAG, "hookHandler: " + e.getMessage());
e.printStackTrace();
}
}
没有贴上不同系统版本的兼容处理,感兴趣的,可以自己查看不同版本的具体实现,从而针对性的进行修改适配;
下一章预告
PackageManagerService
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~