菜鸟带你Hook技术实战

问题

上一篇文章:你想成为Android高级工程师你还得学习Hook中我们提了一个问题:
我们如果要启动一个activity,我们的做法是1. 在AndroidManifest.xml中声明一个Activity 2. startActivity,如果不在AndroidManifest.xml中声明,启动activity会报错(android.content.ActivityNotFoundException)。但是我们想,我们使用插件化,按照正常的思维是不是要将插件化中的所有activity都要声明到清单文件中才可以。所以我们要怎么做才能hook系统启动activity。通过hook我们对AndroidManifest.xml文件中的activity进行偷梁换柱,换成我们没有注册在清单文件中的activity?
下次文章我们为大家详细分析

其实答案就是hook系统启动activity

系统源码流程

通过对系统源码的了解,我们才能知道从哪里hook合适。

对于系统源码这部分,我会随着经验的积累给这些方法里面添加一些核心的内容,但是这部分不影响我们今天的讲解,所以也可以不看这部分,直接看结论就好。

菜鸟带你Hook技术实战_第1张图片

图片来自于(gityuan)

系统源码目录

frameworks/base/services/core/java/com/android/server/am/
  - ActivityManagerService.java
  - ActivityStackSupervisor.java
  - ActivityStack.java
  - ActivityRecord.java
  - ProcessRecord.java

frameworks/base/core/java/android/app/
  - IActivityManager.java
  - ActivityManagerNative.java (内含AMP)
  - ActivityManager.java

  - IApplicationThread.java
  - ApplicationThreadNative.java (内含ATP)
  - ActivityThread.java (内含ApplicationThread)

  - ContextImpl.java

1.1 Activity.startActivity

public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        startActivityForResult(intent, -1);//[见1.2]
    }
}

1.2 Activity.startActivityForResult

public void startActivityForResult(Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        //[见1.3]
        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());
        }
        //此时requestCode =-1
        if (requestCode >= 0) {
            mStartedActivity = true;
        }
        cancelInputsAndStartExitTransition(options);
    } else {
        ...
    }
}

1.3 Instrumentation.execStartActivity()

  • caller: 当前应用的ApplicationThread对象mAppThread;
  • callingPackage: 调用当前ContextImpl.getBasePackageName(),获取当前Activity所在包名;
  • intent: 这便是启动Activity时,传递过来的参数;
  • resolvedType: 调用intent.resolveTypeIfNeeded而获取;
  • resultTo: 来自于当前Activity.mToken
  • resultWho: 来自于当前Activity.mEmbeddedID
  • requestCode = -1;
  • startFlags = 0;
  • profilerInfo = null;
  • options = null;
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();
        ...
        //[见1.4]
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        //检查activity是否启动成功[1.3.1]
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

1.3.1 Instrumentation.checkStartActivityResult()

public class Instrumentation {
     
    /** @hide */
    public static void checkStartActivityResult(int res, Object intent) {
        if (!ActivityManager.isStartResultFatalError(res)) {
            return;
        }

        switch (res) {
            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);
            ...
        }
}

1.4 ActivityManagerProxy.startActivity()

class ActivityManagerProxy implements IActivityManager {
    ...
    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        //通过Binder传递数据
        ...
        //[见2.1]
        intent.writeToParcel(data, 0);//将Intent写入Binder
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        ...
        return result;
    }
    ...
}

==AMP经过binder IPC,进入ActivityManagerNative(简称AMN)。接下来程序进入了system_servr进程,开始继续执行==

2.1 ActivityManagerNative.onTransact()

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    switch (code) {
    case START_ACTIVITY_TRANSACTION:
    {  
      ...
      Intent intent = Intent.CREATOR.createFromParcel(data);
      ...
      //[见2.2]
      int result = startActivity(app, callingPackage, intent, resolvedType,resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
      ...
      return true;
    }
    ...
    }    
}

2.2 ActivityManagerService.startActivity

public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
            false, ALLOW_FULL_ONLY, "startActivity", null);
    //[2.3]
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, false, userId, null, null);
}

2.3 ActivityStackSupervisor.startActivityMayWait()

  • caller = ApplicationThreadProxy, 用于跟调用者进程ApplicationThread进行通信的binder代理类.
  • callingUid = -1;
  • callingPackage = ContextImpl.getBasePackageName(),获取调用者Activity所在包名
  • intent: 这是启动Activity时传递过来的参数;
  • resolvedType = intent.resolveTypeIfNeeded
  • voiceSession = null;
  • voiceInteractor = null;
  • resultTo = Activity.mToken, 其中Activity是指调用者所在Activity, mToken对象保存自己所处的ActivityRecord信息
  • resultWho = Activity.mEmbeddedID, 其中Activity是指调用者所在Activity
  • requestCode = -1;
  • startFlags = 0;
  • profilerInfo = null;
  • outResult = null;
  • config = null;
  • options = null;
  • ignoreTargetSecurity = false;
  • userId = AMS.handleIncomingUser, 当调用者userId跟当前处于同一个userId,则直接返回该userId;当不相等时则根据调用者userId来决定是否需要将callingUserId转换为mCurrentUserId.
  • iContainer = null;
  • inTask = null;
final int startActivityMayWait(IApplicationThread caller, int callingUid, String callingPackage, Intent intent, String resolvedType, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, WaitResult outResult, Configuration config, Bundle options, boolean ignoreTargetSecurity, int userId, IActivityContainer iContainer, TaskRecord inTask) {
    ...
    boolean componentSpecified = intent.getComponent() != null;
    //创建新的Intent对象,即便intent被修改也不受影响
    intent = new Intent(intent);
    ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
    synchronized (mService) {
        //[见2.4]
        int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                voiceSession, voiceInteractor, resultTo, resultWho,
                requestCode, callingPid, callingUid, callingPackage,
                realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                componentSpecified, null, container, inTask);
    }
    ...
    return res;
}

2.4 ActivityStackSupervisor.startActivityLocked()

这里要注意的是如果err = ActivityManager.START_INTENT_NOT_RESOLVED;就会有1.3.1所述代码的异常。

final int startActivityLocked(IApplicationThread caller, Intent intent, String resolvedType, ActivityInfo aInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, Bundle options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) {
    ...
    if (err == ActivityManager.START_SUCCESS && aInfo == null) {
        //从Intent中无法找到相应的ActivityInfo
        err = ActivityManager.START_INTENT_NOT_RESOLVED;
    }
    ...
   //[见2.5]
    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, true, options, inTask);
    ...
    return err;
}

2.5 ActivityStackSupervisor.startActivityUncheckedLocked()

// sourceRecord是指调用者, r是指本次将要启动的Activity
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, Bundle options, TaskRecord inTask) {
    ...
    //创建activity [见2.6]
    targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);   
    ...
}

2.6 ActivityStack.startActivityLocked()

final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) {
    if (doResume) {
        //[见2.7]
        mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
    }
}

2.7 ActivityStackSupervisor.resumeTopActivitiesLocked()

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) {
    ...
    if (isFrontStack(targetStack)) {
        //[见流程2.8]
        result = targetStack.resumeTopActivityLocked(target, targetOptions);
    }
    ...
}

2.8 AS.resumeTopActivityLocked

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
    ...
    //[见2.9]
    result = resumeTopActivityInnerLocked(prev, options);
    return result;
}

2.9 AS.resumeTopActivityInnerLocked()

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
    ...
    //[2.10]
    mStackSupervisor.startSpecificActivityLocked(next, true, true);
    ...
}

2.10 ASS.startSpecificActivityLocked()

void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);
    //真正的启动Activity【见流程2.11】
    realStartActivityLocked(r, app, andResume, checkConfig);
    //当进程不存在则创建进程 [见2.12]
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
            "activity", r.intent.getComponent(), false, false, true);
}

2.11 ASS.realStartActivityLocked()

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException {
    //[见2.12]
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
            System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
            new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
            task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
            newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);

}

2.12 ApplicationThreadProxy.scheduleLaunchActivity()

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {

    //[见3.1]
    mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);
}

3.1 ApplicationThreadNative.onTransact()

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{
    //[见3.1]
    scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo,
            referrer, voiceInteractor, procState, state, persistentState, ri, pi,
            notResumed, isForward, profilerInfo);
}

3.1 ApplicationThread.scheduleLaunchActivity()

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
     //[见3.2]
     sendMessage(H.LAUNCH_ACTIVITY, r);
 }

3.2 ActivityThread.java::H.handleMessage

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);
            //[见3.3]
            handleLaunchActivity(r, null);
        } break;
        ...
    }
}

3.3 ActivityThread.handleLaunchActivity()

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    //最终回调目标Activity的onCreate
    //见[3.4]
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        //最终回调目标Activity的onStart,onResume.
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed);

        if (!r.activity.mFinished && r.startsNotResumed) {
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnPause(r.activity);
            r.paused = true;
        }
    } else {
        //存在error则停止该Activity
        ActivityManagerNative.getDefault()
            .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
    }
}

3.4 ActivityThread.performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        //见[3.5]
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...
    } catch (Exception e) {
        ...
    }
}

3.5 Instrumentation.newActivity()

public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    return (Activity)cl.loadClass(className).newInstance();
}

我们最后看到在ActivityThread.performLaunchActivity()方法中创建了Activity,在这个过程中我分别用1.2.3标记不同的进程,1.3分别是app进程,2是system_server进程。

  • 1标记的进程也就是app进程,主要将要启动的Activity信息(Activity名称,当前所在包名),当前应用的ApplicationThread对象,intent(传递进去的参数)等。
  • 2标记的进程也就是system_server进程,进行各种判断检查,信息保存,组件调度等等
  • 3标记的进程还是app进程,此时主要是创建Activity对象,回调各种回调方法,根据2标记的进程来的信息。

其中ActivityManagerNative.getDefault()得到的是AMS的代理,与AMS进行通信。代码如下:


private static final Singleton gDefault = new Singleton() {
    protected IActivityManager create() {
        //获取名为"activity"的服务,服务都注册到ServiceManager来统一管理
        IBinder b = ServiceManager.getService("activity");
        IActivityManager am = asInterface(b);
        return am;
    }
};
public abstract class Singleton<T> {
     
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                //首次调用create()来获取AMP对象
                mInstance = create();
            }
            return mInstance;
        }
    }
}

ActivityManagerNative.getDefault()

static public IActivityManager getDefault() {
    return gDefault.get();
}

gDefault为Singleton类型对象,此次采用单例模式,mInstance为IActivityManager类的代理对象,即ActivityManagerProxy。


那什么从AMS给APP通信呢?

Activity.startActivity()

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), ...);...
    } else {
        ...
    }
}

我们看到调用startActivityForResult()这个方法的时候使用mMainThread.getApplicationThread()作为参数,这个参数得到的是什么?

public class Activity extends ... {
     
    ActivityThread mMainThread;
}

ActivityThread.java

public final class ActivityThread {
     
    final ApplicationThread mAppThread = new ApplicationThread();
    final H mH = new H();

    public ApplicationThread getApplicationThread()
    {
        return mAppThread;
    }

    /*内部类*/
    private class ApplicationThread extends IApplicationThread.Stub {
     
        private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
            Message msg = Message.obtain();
            msg.what = what;
            msg.obj = obj;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            if (async) {
                msg.setAsynchronous(true);
            }
            mH.sendMessage(msg);
        }
        ...
    }

    /*内部类*/
    private class H extends Handler {
     
        public void handleMessage(Message msg) {
            ...
        }
    }
}

从结构上看出来,是将ActivityThread的内部类ApplicationThread的对象传入AMS中。这个对象处理IApplicationThread接口所有方法都是利用ActivityThread类的成员变量H进行发送消息。也就是AMS通过ApplicationThread对象控制APP进程这端。所以app进程这边的实现逻辑全部在H类中

寻找hook点

我们并没有办法通过hook改变别的进程信息,比如AMS。而且通过源码我们知道最后创建Activity对象也是回归到App进程,所以我们就可以在app进程真正创建Activity对象的时候偷梁换柱。

比如我们有个废掉的Activity叫做StubActivity,这个Activity是在我们清单文件中声明了的,我们要启动TargetActivity。我们需要在Activity检查之前进行拦截,将我们的TargetActivity替换成StubActivity,这样系统就认为我们要启动StubActivity。而我们在hook的时候将TargetActivity信息保存下来,当系统确定要创建Activity的时候,我们在将信息还原。这个时候就创建的是TargetActivity。

所以我们hook点如下:

  1. 对ActivityManagerNative的Singleton gDefault对象中的mInstance进行hook,他是IActivityManager与AMS交互的Binder代理。
  2. 我们队ActivityThread.mH对象进行hook,更换mCallback,使得在LAUNCH_ACTIVITY这种情况偷换为TargetActivity的Intent信息。

项目代码解析

一:需要在清单文件中声明

<application>
    
    <activity android:name=".StubActivity"/>
application>

二:一个空的Activity

public class StubActivity extends Activity{
     
    // dummy
}

三:对两个hook点进行hook

public class AMSHookHelper {
     

    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";

    /**
     * 主要完成的操作是  "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity"
     */
    public static void hookActivityManagerNative() throws Exception {
         Field gDefaultField =null;
        if (Build.VERSION.SDK_INT >= 26) {
            Class activityManager = Class.forName("android.app.ActivityManager");
             gDefaultField = activityManager.getDeclaredField("IActivityManagerSingleton");
        }else{
            Class activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
             gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
        }
        gDefaultField.setAccessible(true);
        Object gDefault = gDefaultField.get(null);
        // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
        Class singleton = Class.forName("android.util.Singleton");
        Field mInstanceField = singleton.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        // ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象
        Object rawIActivityManager = mInstanceField.get(gDefault);
        // 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class iActivityManagerInterface = Class.forName("android.app.IActivityManager");
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));
        mInstanceField.set(gDefault, proxy);
    }

    public static void hookActivityThreadHandler() throws Exception {

        // 先获取到当前的ActivityThread对象
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
        currentActivityThreadField.setAccessible(true);
        Object currentActivityThread = currentActivityThreadField.get(null);

        // 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
        Field mHField = activityThreadClass.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(currentActivityThread);
        Field mCallBackField = Handler.class.getDeclaredField("mCallback");
        mCallBackField.setAccessible(true);

        mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH));

    }
}

四:拦截方法实现

class IActivityManagerHandler implements InvocationHandler {

    private static final String TAG = "IActivityManagerHandler";
    Object mBase;

    public IActivityManagerHandler(Object base) {
        mBase = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("startActivity".equals(method.getName())) {
            Intent raw;
            int index = 0;
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            raw = (Intent) args[index];
            Intent newIntent = new Intent();
            String stubPackage = "com.hookactivity.cn";
            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
            newIntent.setComponent(componentName);
            newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
            args[index] = newIntent;
            Log.d(TAG, "hook success");
            return method.invoke(mBase, args);

        }
        return method.invoke(mBase, args);
    }
}
class ActivityThreadHandlerCallback implements Handler.Callback {

    Handler mBase;
    public ActivityThreadHandlerCallback(Handler base) {
        mBase = base;
    }
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
            case 100:
                handleLaunchActivity(msg);
                break;
        }
        mBase.handleMessage(msg);
        return true;
    }

    private void handleLaunchActivity(Message msg) {
        Object obj = msg.obj;
        try {
            // 把替身恢复成真身
            Field intent = obj.getClass().getDeclaredField("intent");
            intent.setAccessible(true);
            Intent raw = (Intent) intent.get(obj);
            Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
            raw.setComponent(target.getComponent());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

五:主入口

public class MainActivity extends Activity {
     
    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);

        try {
            AMSHookHelper.hookActivityManagerNative();
            AMSHookHelper.hookActivityThreadHandler();
        } catch (Throwable throwable) {
            throw new RuntimeException("hook failed", throwable);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = new Button(this);
        button.setText("启动TargetActivity");

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // TargetActivity并没有在AndroidManifest.xml中声明
                startActivity(new Intent(MainActivity.this, TargetActivity.class));
            }
        });
        setContentView(button);
    }
}

六:目标Activity

不能再清单中注册

public class TargetActivity extends Activity {
     
    private static final String TAG = "TargetActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText("TargetActivity 启动成功!!!");
        setContentView(tv);

    }
}

以上是对系统启动activity的Hook,基本能满足我们的一些特殊的需求

下面我们再讲讲当我们需要hook任意服务的时候应该怎么做呢?

获取服务&注册

ServiceManager.getService()

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name); //先从缓存中查看
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);//[1.2]
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

1.2 ServiceManagerNative.java ::ServiceManagerProxy()

这里可以看到底层的ServiceManager进行交互,通过底层的ServiceManager返回对应服务的IBinder对象

class ServiceManagerProxy implements IServiceManager {
    public IBinder getService(String name) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        //mRemote为BinderProxy
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0); 
        //从reply里面解析出获取的IBinder对象
        IBinder binder = reply.readStrongBinder(); 
        reply.recycle();
        data.recycle();
        return binder;
    }
}

关于底层ServiceManager

//有关代码目录

framework/native/cmds/servicemanager/
  - service_manager.c
  - binder.c

kernel/drivers/ (不同Linux分支路径略有不同)
  - staging/android/binder.c
  - android/binder.c 

关于ServiceManager的启动过程如下:
1. 打开binder驱动:binder_open;
2. 注册成为binder服务的大管家:binder_become_context_manager
3. 进入无限循环,处理client端发来的请求:binder_loop;

ServiceManager是由init进程通过解析init.rc文件而创建的,其所对应的可执行程序/system/bin/servicemanager,所对应的源文件是service_manager.c,进程名为/system/bin/servicemanager

可见ServiceManager相当于一个房屋中介,有人过来租房子,有人过来出租。那么系统服务在什么时候进行注册呢?

系统服务的注册

这一部分在SystemServer中,我们只是举一个例子的代码

//ActivityManagerService.java->setSystemProcess()
public void setSystemProcess() {
    try {
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        if (MONITOR_CPU_USAGE) {
            ServiceManager.addService("cpuinfo", new CpuBinder(this));
        }
        ServiceManager.addService("permission", new PermissionController(this));
        ServiceManager.addService("processinfo", new ProcessInfoService(this));
        ...
    } catch (PackageManager.NameNotFoundException e) {
        ...
    }
}

可见在SystemServer中都是通过ServiceManager.addService()方法将服务添加到大管家中。

SystemServer的启动时机是在Zygote进程启动时候通过抛出异常的办法执行SystemServer.main()方法进入java世界。

启动的一些系统服务如下:

EntropyService:熵(shang)服务,用于产生随机数 
PowerManagerService:电源管理服务 
ActivityManage搜索rService:最核心服务之一,Activity管理服务 
TelephonyRegistry:电话服务,电话底层通知服务 
PackageManagerService:程序包管理服务 
AccountManagerService:联系人帐户管理服务 
ContentService:内容提供器的服务,提供跨进程数据交换 
LightsService:光感应传感器服务 
BatteryService:电池服务,当电量不足时发广播 
VibratorService:震动器服务 
AlarmManagerService:闹钟服务 
WindowManagerService:窗口管理服务 
BluetoothService:蓝牙服务 
InputMethodManagerService:输入法服务,打开关闭输入法 
AccessibilityManagerService:辅助管理程序截获所有的用户输入,并根据这些输入给用户一些额外的反馈,起到辅助的效果,View的点击、焦点等事件分发管理服务 
DevicePolicyManagerService:提供一些系统级别的设置及属性 
StatusBarManagerService:状态栏管理服务 
ClipboardService:粘贴板服务 
NetworkManagementService:手机网络管理服务 
TextServicesManagerService: 
NetworkStatsService:手机网络状态服务 
NetworkPolicyManagerService: 
WifiP2pService:Wifi点对点直联服务 
WifiService:WIFI服务 
ConnectivityService:网络连接状态服务 
ThrottleService:modem节流阀控制服务 
MountService:磁盘加载服务,通常也mountd和vold服务结合 
NotificationManagerService:通知管理服务,通常和StatusBarManagerService 
DeviceStorageMonitorService:存储设备容量监听服务 
LocationManagerService:位置管理服务 
CountryDetectorService:检查当前用户所在的国家 
SearchManagerService:搜索管理服务 
DropBoxManagerService:系统日志文件管理服务(大部分程序错误信息) 
WallpaperManagerService:壁纸管理服务 
AudioService:AudioFlinger上层的封装的音量控制管理服务 
UsbService:USB Host和device管理服务 
UiModeManagerService:UI模式管理服务,监听车载、座机等场合下UI的变化 
BackupManagerService:备份服务 
AppWidgetService:应用桌面部件服务 
RecognitionManagerService:身份识别服务 
DiskStatsService:磁盘统计服务 
SamplingProfilerService:性能统计服务 
NetworkTimeUpdateService:网络时间更新服务

研究系统服务的Hook点

我们在应用中获取系统服务使用如下代码获取AMS的代理服务:

ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
}

可以看到从一个Map结构中根据名字得到service,那么一个服务在java层怎么注册的呢?

registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
                    IAccountManager service = IAccountManager.Stub.asInterface(b);
                    return new AccountManager(ctx, service);
                }});
registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
                }});

可以看出,核心不变,只是简单包装成了一个ServiceFetcher对象而已。

但是我们对于ActivityManager的使用,查看startActivity/startService就会知道我们并没有像上面一样获取AM服务,而是通过ActivityManagerNative.getDefault()完成

private static final Singleton gDefault = new Singleton() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            IActivityManager am = asInterface(b);//进行如下调用
            return am;
        }
    };


static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    return new ActivityManagerProxy(obj);
}

queryLocalInterface()方法的目的是如果是服务端则直接返回服务端Binder对象,如果是客户端,则返回null,进而通过asInterface()返回代理对象。

所以综上所述服务的获取套路是:

IBinder b = ServiceManager.getService("service_name"); // 获取原始的IBinder对象
IXXInterface in = IXXInterface.Stub.asInterface(b); // 转换为Service接口

Hook点总结:

我们看到最终返回什么还是由于queryLocalInterface()方法决定,我们只要拦截该方法,让其一直返回我们的代理对象从而asInterface()就会一直返回我们代理的对象达到Hook目的。

举个栗子

剪贴板使我们的系统服务,我们就对其进行改造

  1. 得到剪贴板服务
final String CLIPBOARD_SERVICE = "clipboard";
Class serviceManager = Class.forName("android.os.ServiceManager");
Method getService = serviceManager.getDeclaredMethod("getService", String.class);
IBinder rawBinder = (IBinder) getService.invoke(null, CLIPBOARD_SERVICE);
  1. 对ServiceManager进行hook
IBinder hookedBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),
        new Class[] { IBinder.class },
        new BinderProxyHookHandler(rawBinder));
  1. 用我们自己的剪贴板服务代替系统剪贴板服务
Field cacheField = serviceManager.getDeclaredField("sCache");
cacheField.setAccessible(true);
Map cache = (Map) cacheField.get(null);
cache.put(CLIPBOARD_SERVICE, hookedBinder);
  1. 我们hook处理的逻辑
public class BinderProxyHookHandler implements InvocationHandler {
     
    private static final String TAG = "BinderProxyHookHandler";
    IBinder base;
    Class stub;
    Class iinterface;

    public BinderProxyHookHandler(IBinder base) {
        this.base = base;
        try {
            this.stub = Class.forName("android.content.IClipboard$Stub");
            this.iinterface = Class.forName("android.content.IClipboard");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("queryLocalInterface".equals(method.getName())) {
            return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
                    new Class[] { IBinder.class, IInterface.class, this.iinterface },
                    new BinderHookHandler(base, stub));
        }
        return method.invoke(base, args);
    }
}

写到这块,我突然又一次不理解动态代理了,我先说下不理解的原因步骤:

  • 由于我们最初代理的是ServiceManager
  • 然后我们在对应BinderProxyHookHandler的invoke方法中是可以拦截queryLocalInterface方法,这点没错。
  • 如果我们在invoke中不做任何处理返回的是IBinder对应接口方法中的那些原有实现。
  • 但是此时我们对queryLocalInterface做了特殊处理,意味着执行到该方法的时候返回我们特殊处理的结果。

- 此时特殊处理的结果又是一个代理。使用的参数是我们ServiceManager的ClassLoader,我们不是要返回我们自己的剪贴板服务吗。怎么返回一个代理,这个代理传入的接口是IClipboard。

怎么理解这里!!!

通过查阅资料,原因是我没有理解动态代理,这里的代理意思。

本质上,这里通过Proxy.newProxyInstance(classLoader,interface,handle),其中
- classLoader只需要和接口的interface保持一致,因为系统会自动生成一个类继承这些接口
- interface提供的是这些接口内部需要系统生成类的方法,也就是那个生成的类中会实现这些方法
- handle是专门处理这些方法的实现。

所以在这里当调用到IBinder中的queryLocalInterface()方法时候,我们生成了一个对IClipboard的代理,当执行IClipboard里面的每个方法的时候都回去BinderHookHandler拦截。所以代理返回的对象其实是系统自动为我们生成的实现那些接口的一个对象。

点这里看原因

  1. 最终进行处理
public class BinderHookHandler implements InvocationHandler {
     
    private static final String TAG = "BinderHookHandler";
    // 原始的Service对象 (IInterface)
    Object base;
    public BinderHookHandler(IBinder base, Class stubClass) {
        try {
            Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class);
            // IClipboard.Stub.asInterface(base);
            this.base = asInterfaceMethod.invoke(null, base);
        } catch (Exception e) {
            throw new RuntimeException("hooked failed!");
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 把剪切版的内容替换为 "you are hooked"
        if ("getPrimaryClip".equals(method.getName())) {
            return ClipData.newPlainText(null, "you are hooked");
        }
        // 欺骗系统,使之认为剪切版上一直有内容
        if ("hasPrimaryClip".equals(method.getName())) {
            return true;
        }
        return method.invoke(base, args);
    }
}

此时我们已经把系统对于一个服务的hook点搞清楚了,那下来我们就多研究几个hook系统服务的点。

Hook其他系统服务

这个时候我们就不适用上面那么low的代码进行hook了,我把大神们写的DroidPlugin框架进行拆解,我们只进行一个服务的hook,其他服务按照框架任意添加,这样我们就能够处理任何服务的任何方法了。是不是想想很爽。

Hook NotificationManager

public class APP extends Application{
     
    @Override
    public void onCreate() {
        super.onCreate();
        try {
            PluginProcessManager.installHook(this);//入口进行装载hook
            PluginProcessManager.setHookEnable(true);//设置hook可用
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

如何使用

就按照我们正常的使用逻辑就好,出现的效果就是当我们点击和取消toast的时候在后台有对应信息打印

public class MainActivity extends AppCompatActivity {
     
    private Button start;
    private Button end;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start = (Button) findViewById(R.id.start);
        end = (Button) findViewById(R.id.end);
        final Toast toast = Toast.makeText(MainActivity.this, "toast", Toast.LENGTH_LONG);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toast.show();
            }
        });
        end.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toast.cancel();
            }
        });
    }
}

Hook类

这个类是Hook的基类,应该由控制能否hook的变量,和context,还有hook处理控制

public abstract class Hook {
     
    private boolean mEnable = false;//是否能进行Hook
    protected Context mHostContext;
    protected BaseHookHandle mHookHandles;//掌控Hook

    public void setEnable(boolean enable, boolean reInstallHook) {
        this.mEnable = enable;
    }
    public final void setEnable(boolean enable) {
        setEnable(enable, false);
    }
    public boolean isEnable() {
        return mEnable;
    }
    protected Hook(Context hostContext) {
        mHostContext = hostContext;
        mHookHandles = createHookHandle();
    }
    protected abstract BaseHookHandle createHookHandle();
    protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
    protected void onUnInstall(ClassLoader classLoader) throws Throwable {
    }
}

对Binder的hook

只要是关于binder的hook,都需要继承该基类

abstract class BinderHook extends Hook implements InvocationHandler {

    private Object mOldObj;//原IBinder服务代理对象
    public BinderHook(Context hostContext) {
        super(hostContext);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (!isEnable()) {
                return method.invoke(mOldObj, args);
            }
            //通过Hook基类得到的方法处理类
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            if (hookedMethodHandler != null) {
                //传入原系统服务对象
                return hookedMethodHandler.doHookInner(mOldObj, method, args);
            } else {
                return method.invoke(mOldObj, args);
            }
        } catch (Exception e) {}
    }

    abstract Object getOldObj() throws Exception;

    void setOldObj(Object mOldObj) {
        this.mOldObj = mOldObj;
    }

    public abstract String getServiceName();

    //方法的入口
    @Override
    protected void onInstall(ClassLoader classLoader) throws Throwable {
        new ServiceManagerCacheBinderHook(mHostContext, getServiceName()).onInstall(classLoader);//先进行服务代理IBinder的替换
        mOldObj = getOldObj();//得到原始IBinder服务对象
        Class clazz = mOldObj.getClass();
        List> interfaces = Utils.getAllInterfaces(clazz);
        Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
        Object proxiedObj = MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);
        MyServiceManager.addProxiedObj(getServiceName(), proxiedObj);//添加处理过的IBinder代理对象
    }
}

当执行过该类的onInstall方法之后,产生的结果就是将app进程中serviceManager对应的缓存中IBinder替换成我们自己的代理对象,并且当使用的时候进行我们mHookHandles的处理。

子类只是进行一些所需类进行实现的。

public class INotificationManagerBinderHook extends BinderHook {
     
    public static final String SERVICE_NAME = "notification";
    public INotificationManagerBinderHook(Context hostContext) {
        super(hostContext);
    }
    @Override
    protected BaseHookHandle createHookHandle() {
        return new INotificationManagerHookHandle(mHostContext);
    }
    public Object getOldObj() throws Exception {
        IBinder iBinder = MyServiceManager.getOriginService(SERVICE_NAME);
        return INotificationManagerCompat.asInterface(iBinder);
    }
    public String getServiceName() {
        return SERVICE_NAME;
    }
}

我们在BinderHook.onInstall()首先要进行服务的替换,那么服务的替换就是下面这个核心类:


public class ServiceManagerCacheBinderHook extends Hook implements InvocationHandler {
     

    private String TAG = ServiceManagerCacheBinderHook.class.getSimpleName();
    private String mServiceName;
    public ServiceManagerCacheBinderHook(Context hostContext, String servicename) {
        super(hostContext);
        mServiceName = servicename;
        setEnable(true);
    }
    @Override
    protected void onInstall(ClassLoader classLoader) throws Throwable {
        //拿到ServiceManager中的服务IBinder缓存列表
        Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache");
        if (sCacheObj instanceof Map) {
            Map sCache = (Map) sCacheObj;
            Object Obj = sCache.get(mServiceName);//得到原始服务对象
            if (Obj != null && false) {
                throw new RuntimeException("Can not install binder hook for " + mServiceName);
            } else {
                sCache.remove(mServiceName);//根据名称移除掉原有服务
                IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName);//看移除了能否拿到原有服务
                if (mServiceIBinder == null) {
                    if (Obj != null && Obj instanceof IBinder && !Proxy.isProxyClass(Obj.getClass())) {
                        mServiceIBinder = ((IBinder) Obj);
                    }
                }
                if (mServiceIBinder != null) {
                    MyServiceManager.addOriginService(mServiceName, mServiceIBinder);//将原始服务IBinder存入缓存
                    Class clazz = mServiceIBinder.getClass();
                    List> interfaces = Utils.getAllInterfaces(clazz);
                    Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                    IBinder mProxyServiceIBinder = (IBinder) MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);//服务代理类
                    sCache.put(mServiceName, mProxyServiceIBinder);//代替系统的服务
                    MyServiceManager.addProxiedServiceCache(mServiceName, mProxyServiceIBinder);//加入代理服务中
                }
            }
        }
    }
    /**
     * 这个方法代理的是原IBinder代理的服务类所有方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            IBinder originService = MyServiceManager.getOriginService(mServiceName);
            if (!isEnable()) {
                return method.invoke(originService, args);
            }
            //对其进行拦截
            HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
            if (hookedMethodHandler != null) {
                return hookedMethodHandler.doHookInner(originService, method, args);
            } else {
                return method.invoke(originService, args);
            }
        } catch (Exception e) {
        }
    }

    private class ServiceManagerHookHandle extends BaseHookHandle {
     
        private ServiceManagerHookHandle(Context context) {
            super(context);
        }
        @Override
        protected void init() {
            //此方法是:查看本进程是不是存在这个Binder对象
            //android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            sHookedMethodHandlers.put("queryLocalInterface", new queryLocalInterface(mHostContext));
        }
        class queryLocalInterface extends HookedMethodHandler {
            public queryLocalInterface(Context context) {
                super(context);
            }
            @Override
            protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
                Object localInterface = invokeResult;//由于已经在缓存中将代理服务移除,所以此处为null
                //模仿的是asInterface()这个方法
                Object proxiedObj = MyServiceManager.getProxiedObj(mServiceName);
                if (localInterface == null && proxiedObj != null) {
                    setFakedResult(proxiedObj);
                }
            }
        }
    }
    @Override
    protected BaseHookHandle createHookHandle() {
        return new ServiceManagerHookHandle(mHostContext);
    }
}

目的就是替换

通过这种结构,我们就将处理进行独立,每次hook一个服务只需要继承BinderHook即可。
详细代码请看DroidPlugin框架。如需我独立出来的框架进行学习微信关注:码老板
菜鸟带你Hook技术实战_第2张图片

你可能感兴趣的:(Android应用开发,Android,高级Android,hook)