Hook 实战之 Hook AMS
共 12606字,需浏览 26分钟
·
2020-11-16 11:32
点击上方
Android波斯湾
订阅,随时接受最新的干货推送。喜爱人家就标星一下嘛~
本文 Android 系统源码基于 9.0
我们知道新建一个 Activity 之后我们需要在 manifest 中注册,否则启动的时候就会崩溃,现在使用 Hook 的方法绕过检查来启动一个没有注册的 Activity。
如果我们不注册的话就会报下面的错误:
Caused by: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.jeanboy.app.hooktrainning/com.jeanboy.app.hooktrainning.UnregisterActivity}; have you declared this activity in your AndroidManifest.xml?
寻找第一个 Hook 点
startActivity(new Intent(this, UnregisterActivity.class));
我们从 startActivity() 可以看到:
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
// 关键代码
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
可以看到进入到了 Instrumentation 这个类中的 execStartActivity() 方法。
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
// ...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
可以看到 ActivityManager.getService() 是拿到 ActivityManagerService 服务在本地的代理对象,然后通过它操作 ActivityManagerService 执行 startActivity() 方法返回一个结果,最后执行 checkStartActivityResult() 方法。
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);
// ...
}
}
在 checkStartActivityResult() 方法中可以看到,当 res 返回是 START_CLASS_NOT_FOUND 的时候就会报出一开始的错误了。因为我们传过去的 Activity ActivityManagerService 找不到。
所以我们就可以把检查方法之前的 ActivityManager.getService().startActivity() 作为一个 Hook 点,我们给它随便传一个注册过的 Acivity,这样就可以欺骗 ActivityManagerService 了。
ActivityManager.getService()
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityManager.Stub.asInterface(b);
}
};
- Singleton
public abstract class Singleton<T> {
public Singleton() { }
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
可以看到 Singleton 是一个系统的单例类,getService()
方法调用的时候,就会 create() 方法,最终会调用 IActivityManagerSingleton 中的 create() 方法创建一个 IActivityManager 返回。
IActivityManager 就是 ActivityManagerService 在本地的代理对象。用来进行进程间的 Binder 通信。
Hook IActivityManager
我们来 Hook IActivityManager,替换成我们自己的。
先定义一个空的 ProxyActivity,并在 AnroidManifest 中注册:
public class ProxyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_proxy);
}
}
然后在 Application 中 Hook 住 ActivityManagerService。
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HookAMS.hookStartActivity(this);
}
}
- HookAMS
public class HookAMS {
public static void hookStartActivity(final Context context) {
try {
// 获取到 ActivityTaskManager 的 Class 对象
@SuppressLint("PrivateApi")
Class> amClass = Class.forName("android.app.ActivityManager");
// 获取到 IActivityTaskManagerSingleton 成员变量
Field iActivityTaskManagerSingletonField = amClass.getDeclaredField("IActivityManagerSingleton");
iActivityTaskManagerSingletonField.setAccessible(true);
// 获取 IActivityTaskManagerSingleton 成员变量的值
Object IActivityTaskManagerSingleton = iActivityTaskManagerSingletonField.get(null);
// 获取 getService() 方法
@SuppressLint("BlockedPrivateApi")
Method getService = amClass.getDeclaredMethod("getService");
getService.setAccessible(true);
// 执行 getService() 方法
final Object IActivityTaskManager = getService.invoke(null);
// 获取到 IActivityTaskManager 的 Class 对象
@SuppressLint("PrivateApi")
Class> iamClass = Class.forName("android.app.IActivityManager");
// 创建代理类 IActivityTaskManager
Object proxyIActivityManager = Proxy.newProxyInstance(context.getClassLoader(), new Class[]{iamClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
Intent proxyIntent = new Intent(context, ProxyActivity.class);
// startActivity 第三个参数为 Intent
proxyIntent.putExtra("targetIntent", (Intent) args[2]);
args[2] = proxyIntent;
}
return method.invoke(IActivityTaskManager, args);
}
});
// 获取到 Singleton 的 Class 对象
@SuppressLint("PrivateApi")
Class> sClass = Class.forName("android.util.Singleton");
// 获取到 mInstance 成员变量
Field mInstanceField = sClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// 赋值 proxyIActivityManager 给 mInstance 成员变量
mInstanceField.set(IActivityTaskManagerSingleton, proxyIActivityManager);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
我们的目的很清楚,通过反射拿到 IActivityManager 的实例,然后把它替换成我们自己的 proxyIActivityManager。动态代理对象中,我们把 intent 替换成一个注册过的 Activity 也就是 ProxyActivity。现在我们就拦截住了,当我们跳转到 UnregisterActivity 这个没有注册的 Activity 的时候,就会先跳转到该 ProxyActivity。
当然这不是我们想要的效果,我们需要在检查完之后再给它替换回来,所以在检查完后还要 Hook 一个地方给它换回来。
寻找第二个 Hook 点
熟悉 Activity 的启动流程的都知道,ActivityManagerService 处理完成之后,会执行到 realStartActivityLocked()。最终会回到 ActivityThread 类中的 mH 这个 Handler 中进行最后的处理。
imgclass H extends Handler {
// ...
public void handleMessage(Message msg) {
switch (msg.what) {
// ...
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
}
}
ClientTransaction 内部有一个 ClientTransactionItem 的集合。
public class ClientTransaction implements Parcelable, ObjectPoolItem {
private List mActivityCallbacks;
// ...
}
在 realStartActivityLocked() 方法中可以看到将一个 LaunchActivityItem 添加到 ClientTransaction 中的集合中,也就是 mActivityCallbacks 中。
public class LaunchActivityItem extends ClientTransactionItem {
private Intent mIntent;
private int mIdent;
private ActivityInfo mInfo;
private Configuration mCurConfig;
private Configuration mOverrideConfig;
private CompatibilityInfo mCompatInfo;
private String mReferrer;
private IVoiceInteractor mVoiceInteractor;
private int mProcState;
private Bundle mState;
private PersistableBundle mPersistentState;
private List mPendingResults;
private List mPendingNewIntents;
private boolean mIsForward;
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
// ...
}
LaunchActivityItem 中存储了 Activity 的各种信息,这里有一个 mIntent 参数,它现在的跳转是我们在上一个 Hook 点改变成的 ProxyActivity,所以这里我们需要重新给他还原会我们的 UnregisterActivity,这样才能顺利跳转到 UnregisterActivity 中。
因此,我们需要在执行 Handler 中的 handleMessage() 方法之前将它给改了。
我们知道 Handler 的消息分发机制中有一个 dispatchMessage() 方法:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Activity 的启动最终会执行 handleMessage() 方法,而在这个之前有一个判断,如果 mCallback 不为 null 就执行 mCallback.handleMessage(msg) 方法。所以我们可以给它传一个我们自己的 CallBack,在内部将 mIntent 给改了,然后返回 false 它还是会继续执行下面的 handleMessage 方法,这样就完成了替换。
Hook ActivityThread
然后在 Application 中 Hook 住 ActivityThread。
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HookAMS.hookStartActivity(this);
HookAMS.hookActivityThread();
}
}
- hookActivityThread()
public class HookAMS {
// ...
public static void hookActivityThread() {
try {
// 获取到 mH 对象
@SuppressLint("PrivateApi")
Class> atClass = Class.forName("android.app.ActivityThread");
Field mHField = atClass.getDeclaredField("mH");
mHField.setAccessible(true);
// 获取到 ActivityThread 对象
@SuppressLint("DiscouragedPrivateApi")
Method currentActivityThreadMethod = atClass.getDeclaredMethod("currentActivityThread");
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
Object mH = mHField.get(currentActivityThread);
// 拿到 mCallback 替换成我们自己的
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mH, new MyCallback());
} catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
private static class MyCallback implements Handler.Callback {
@Override
public boolean handleMessage(@NonNull Message msg) {
Object clientTransactionObj = msg.obj;
try {
@SuppressLint("PrivateApi")
Class> laiClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
Field mActivityCallbacksField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List activityCallbackList = (List) mActivityCallbacksField.get(clientTransactionObj);
if (activityCallbackList == null || activityCallbackList.size() == 0) {
return false;
}
Object mLaunchActivityItem = activityCallbackList.get(0);
if (!laiClass.isInstance(mLaunchActivityItem)) {
return false;
}
Field mIntentField = laiClass.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
// 获取代理的 Intent
Intent proxyIntent = (Intent) mIntentField.get(mLaunchActivityItem);
if (proxyIntent == null) {
return false;
}
// 获取到前面传入的 targetIntent
Intent targetIntent = proxyIntent.getParcelableExtra("targetIntent");
if (targetIntent != null) {
// 替换 Intent
mIntentField.set(mLaunchActivityItem, targetIntent);
}
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}
}
这样就完成了对 ActivityManagerService 的欺骗,可以启动没有在 manifest 中注册过的 Activity 了。
技术交流群
欢迎加入技术交流群,来一起交流学习。
我的公众号
欢迎关注我的公众号,分享各种技术干货,各种学习资料,职业发展和行业动态。
参考资料
- Andorid-Hook进阶
- Android插件化——高手必备的Hook技术