Hook技術(四)對系統啟動Activity進行Hook之偷樑換柱Activity
引出問題
我們如果要啟動一個activity,我們的做法是1. 在AndroidManifest.xml
中宣告一個Activity 2. startActivity
,如果不在AndroidManifest.xml
中宣告,啟動activity會報錯(android.content.ActivityNotFoundException
)。但是我們想,我們使用外掛化,按照正常的思維是不是要將外掛化中的所有activity都要宣告到清單檔案中才可以。所以引出了我們今天的知識點:hook系統啟動activity。通過hook我們對AndroidManifest.xml
檔案中的activity進行偷樑換柱,換成我們沒有註冊在清單檔案中的activity,這樣就達到了我們的目的。
系統原始碼流程
通過對系統原始碼的瞭解,我們才能知道從哪裡hook合適。
對於系統原始碼這部分,我會隨著經驗的積累給這些方法裡面新增一些核心的內容,但是這部分不影響我們今天的講解,所以也可以不看這部分,直接看結論就好。
圖片來自於(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<ResultInfo> pendingResults, List<ReferrerIntent> 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<ResultInfo> pendingResults, List<ReferrerIntent> 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<IActivityManager> gDefault = new Singleton<IActivityManager>() {
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點如下:
- 對ActivityManagerNative的
Singleton gDefault
物件中的mInstance進行hook,他是IActivityManager
與AMS互動的Binder代理。 - 我們隊ActivityThread.mH物件進行hook,更換mCallback,使得在LAUNCH_ACTIVITY這種情況偷換為TargetActivity的Intent資訊。
專案程式碼解析
一:需要在清單檔案中宣告
<application>
<!--隨便宣告一個替身Activity-->
<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);
}
}