热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

androidhook模拟点击_手把手讲解AndroidHook实现无清单启动Activity

手把手讲解系列文章,是我写给各位看官,也是写给我自己的。文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年&#

手把手讲解系列文章,是我写给各位看官,也是写给我自己的。

文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年,不能老吸血,也到了回馈开源的时候.

这个系列的文章:

1、用通俗易懂的讲解方式,讲解一门技术的实用价值

2、详细书写源码的追踪,源码截图,绘制类的结构图,尽量详细地解释原理的探索过程

3、提供Github 的 可运行的Demo工程,但是我所提供代码,更多是提供思路,抛砖引玉,请酌情cv

4、集合整理原理探索过程中的一些坑,或者demo的运行过程中的注意事项

5、用gif图,最直观地展示demo运行效果

如果觉得细节太细,直接跳过看结论即可。

本人能力有限,如若发现描述不当之处,欢迎留言批评指正。

学到老活到老,路漫漫其修远兮。与众君共勉 !

引子前面两篇Hook博文,写了两个demo,一个是hook入门,一个是略微复杂的Activity启动流程的hook。

那么玩点更高端的吧, 正常开发中,所有Activity都要在 AndroidManifest,xml中进行注册,才可以正常跳转,通过hook,可以绕过系统对activity注册的检测,即使不注册,也可以正常跳转。

鸣谢感谢大神的博文 https://www.jianshu.com/p/eb4121b037e2

本文中的所有内容,在这位大神的文章中基本都有提及,只是大佬写的东西我看了老久才理解,所以想按照自己的理解写出一篇更通俗易懂的攻略.

另外,大佬使用SDK 27进行hook开发,我则是使用SDK 28,所以我的最终Demo中,copy了大佬博文里面的Activity mH的hook核心代码,和我自己的SDK 28的Activity mH的hook核心代码进行了版本兼容设计.

正文大纲1.整体思路

2.源码索引

3.hook核心代码

4. 最终效果

正文

提示:本文所有源码索引图,都基于SDK 28 -android9.0系统.

1.整体思路在之前Activity启动流程的hook的Demo里,我进行了Activity流程的hook,最终采用的方案,是Hook了AMS,实现了全局的startActivity动作的劫持. 现在就从这个AMS的hook为起点,来实现无清单启动Activity.

在Activity启动流程的hook的Demo里,最后实现的效果是,每次跳转Activity,都能看到这个日志:

image.png

那么,我们既然侦测到了startActivity这个方法的调用,那么自然就可以拿到里面的实参,比如,Intent。

Intent是跳转意图,从哪里来,跳到哪里去的信息,都包含在Intent里面.

而,manifest Activity的检测,也是要根据Intent里面的信息来的.

所以,要骗过系统,要假装我们跳的Activity是已经注册过的,那么只需要将Intent里面的信息换成 已经在manifest中注册的某个Activity就可以了(这里可能就有人像抬杠了,你怎么知道manifest里面一定有注册Activity....如果一个Activity都没有,你的app是怎么启动的呢,至少得有一个LauncherActivity吧 - -!).确定思路:

1.在AMS的hook函数中,将 真实的Intent中的信息,替换成manifest中已有的Activity信息. 骗过系统的检测机制。

2.虽然骗过了系统的检测机制,但是这么一来,每一次的跳转,都会跳到"假"的Activity,这肯定不是我们想要的效果,那么就必须,在真正的跳转时机之前,将 真实的Activity信息,还原回去, 跳到原本该去的Activity.对应的核心代码,其实也就两句话:

image.png

2.源码索引

下图大致画出了:从 Activity.startActivity动作开始,到最终 跳转动作的最终执行者 全过程.

方案1(改).png

下面开始看源码,从Activity.startActivity开始:

image.png

image.png

image.png

image.png

这里开始分支:if(mParent==null),但是两个分支最终执行如下:

true分支

false分支

很显然,两者都是同样的调用过程:Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(....);

mMainThread.sendActivityResult(...);第一句,execStartActivity 是 对一些参数的合法性校验,如果不合法,那就会直接抛出异常,比如之前的

image.png

第二句,sendActivityResult才是真正的跳转动作执行者

先进入第一句Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity看看,既然是合法性校验,且看他是如何校验的。这是Instrumentation的execStartActivity方法

image.png

结论:它是通过AMS去校验的,AMS startActivity会返回一个int数值,随后,checkStartActivityResult方法会根据这个int值,抛出响应的异常,或者什么都不做.

image.png

再进入第二句mMainThread.sendActivityResult看真正的跳转动作是如何执行的:

ps:这里其实有个诀窍,既然我们的终极目标是要骗过系统的Activity Intent检测,那么,跟着Intent这个变量,就不会偏离方向.

image.png

既然intent被封装到了ClientTransaction,交给了mAppThread,那么继续:

image.png

前方有坑,请注意:androidStudio里并不能直接跳转,所以要手动,找到下图中的方法,这个ClientTransactionHandler是ActivityThread的父类.

image.png

上图中,调用了sendMessage(int,Object),在ActivityThread中找到这个方法的实现:

image.png

找它的最终实现:

image.png

找到另一个关键点:mH ,

image.png

H类的定义:(太长了,我就不完整截图了,留下关键的信息)final H mH = new H();class H extends Handler {

...        public static final int EXECUTE_TRANSACTION = 159;        String codeToString(int code) {            if (DEBUG_MESSAGES) {                switch (code) {                    case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";                    case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";

}

}            return Integer.toString(code);

}        public void handleMessage(Message msg) {            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));            switch (msg.what) {                case EXECUTE_TRANSACTION:                    final ClientTransaction transaction = (ClientTransaction) msg.obj;

mTransactionExecutor.execute(transaction);                    if (isSystem()) {                        // Client transactions inside system process are recycled on the client side

// instead of ClientLifecycleManager to avoid being cleared before this

// message is handled.

transaction.recycle();

}                    // TODO(lifecycler): Recycle locally scheduled transactions.

break;

...

}

Object obj = msg.obj;            if (obj instanceof SomeArgs) {

((SomeArgs) obj).recycle();

}            if (DEBUG_MESSAGES) Slog.v(TAG, "<<

}

}

很明显&#xff0c;他就是一个Handler的普通子类&#xff0c;定义了主线程ActivityThread中可能发生的各种事件。

PS: 这里&#xff0c;我留下了case EXECUTE_TRANSACTION:分支&#xff0c;是因为&#xff0c;之前ClientTransactionHandler 抽象类里面&#xff0c;sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);,就是用的这个 EXECUTE_TRANSACTION常量。

终于找到了startActivity的最终执行代码&#xff01;final ClientTransaction transaction &#61; (ClientTransaction) msg.obj;

mTransactionExecutor.execute(transaction);

Ok&#xff0c;就到这里了.(事实上&#xff0c;我本来还想往下追查&#xff0c;Intent被封装到ClientTransaction之后&#xff0c;又被得到了什么样的处理&#xff0c;最后发现居然查到了一个源码中都不存在的类&#xff0c;我表示看不懂了&#xff0c;就到这里吧&#xff0c;不影响我们hook)

3.hook核心代码

还记得我们的整体思路么&#xff1f;1.在AMS的hook函数中&#xff0c;将 真实的Intent中的信息&#xff0c;替换成manifest中已有的Activity信息. 骗过系统的检测机制。

2.虽然骗过了系统的检测机制&#xff0c;但是这么一来&#xff0c;每一次的跳转&#xff0c;都会跳到"假"的Activity,这肯定不是我们想要的效果&#xff0c;那么就必须&#xff0c;在真正的跳转时机之前&#xff0c;将 真实的Activity信息&#xff0c;还原回去, 跳到原本该去的Activity.

说通俗一点就是&#xff0c;

第一&#xff0c;伪造一个Intent&#xff0c;骗过Activity Manifest检测。

第二&#xff0c;真正要跳转之前&#xff0c;把原始的Intent还原回去.

开始撸代码&#xff0c;大量反射代码即将到来&#xff0c;注释应该很详尽了&#xff0c;特别注意&#xff1a;看反射代码要对照源代码来看&#xff0c;不然很容易走神&#xff1a;伪造intent&#xff0c;骗过Activity Manifest检测

这里&#xff0c;请对照&#xff1a;ActivityManager.java 的 4125-4137行

hook对照源代码.png

hook核心代码如下/**

* 这里对AMS进行hook

*

* &#64;param context

*/

private static void hookAMS(Context context) {        try {

Class> ActivityManagerClz;

final Object IActivityManagerObj;//这个就是AMS实例

Method getServiceMethod;

Field IActivityManagerSingletonField;            if (ifSdkOverIncluding26()) {//26&#xff0c;27&#xff0c;28的ams获取方式是通过ActivityManager.getService()

ActivityManagerClz &#61; Class.forName("android.app.ActivityManager");

getServiceMethod &#61; ActivityManagerClz.getDeclaredMethod("getService");

IActivityManagerSingletonField &#61; ActivityManagerClz.getDeclaredField("IActivityManagerSingleton");//单例类成员的名字也不一样

} else {//25往下&#xff0c;是ActivityManagerNative.getDefault()

ActivityManagerClz &#61; Class.forName("android.app.ActivityManagerNative");

getServiceMethod &#61; ActivityManagerClz.getDeclaredMethod("getDefault");

IActivityManagerSingletonField &#61; ActivityManagerClz.getDeclaredField("gDefault");//单例类成员的名字也不一样

}

IActivityManagerObj &#61; getServiceMethod.invoke(null);//OK&#xff0c;已经取得这个系统自己的AMS实例

// 2.现在创建我们的AMS实例

// 由于IActivityManager是一个接口&#xff0c;那么其实我们可以使用Proxy类来进行代理对象的创建

// 结果被摆了一道&#xff0c;IActivityManager这玩意居然还是个AIDL&#xff0c;动态生成的类&#xff0c;编译器还不认识这个类&#xff0c;怎么办&#xff1f;反射咯

Class> IActivityManagerClz &#61; Class.forName("android.app.IActivityManager");            // 构建代理类需要两个东西用于创建伪装的Intent

String packageName &#61; Util.getPMName(context);            String clz &#61; Util.getHostClzName(context, packageName);            Object proxyIActivityManager &#61;                    Proxy.newProxyInstance(

Thread.currentThread().getContextClassLoader(),                            new Class[]{IActivityManagerClz},                            new ProxyInvocation(IActivityManagerObj, packageName, clz));            //3.拿到AMS实例&#xff0c;然后用代理的AMS换掉真正的AMS&#xff0c;代理的AMS则是用 假的Intent骗过了 activity manifest检测.

//偷梁换柱

IActivityManagerSingletonField.setAccessible(true);            Object IActivityManagerSingletonObj &#61; IActivityManagerSingletonField.get(null);

Class> SingletonClz &#61; Class.forName("android.util.Singleton");//反射创建一个Singleton的class

Field mInstanceField &#61; SingletonClz.getDeclaredField("mInstance");

mInstanceField.setAccessible(true);

mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);

} catch (Exception e) {

e.printStackTrace();

}

}

private static final String ORI_INTENT_TAG &#61; "origin_intent";    /**

* 把InvocationHandler的实现类提取出来&#xff0c;因为这里包含了核心技术逻辑&#xff0c;最好独立&#xff0c;方便维护

*/

private static class ProxyInvocation implements InvocationHandler {        Object amsObj;        String packageName;//这两个String是用来构建Intent的ComponentName的

String clz;

public ProxyInvocation(Object amsInstance, String packageName, String clz) {            this.amsObj &#61; amsInstance;            this.packageName &#61; packageName;            this.clz &#61; clz;

}

&#64;Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            //proxy是创建出来的代理类&#xff0c;method是接口中的方法&#xff0c;args是接口执行时的实参

if (method.getName().equals("startActivity")) {

Log.d("GlobalActivityHook", "全局hook 到了 startActivity");

Intent currentRealIntent &#61; null;//侦测到startActivity动作之后&#xff0c;把intent存到这里

int intentIndex &#61; -1;                //遍历参数&#xff0c;找到Intent

for (int i &#61; 0; i 

currentRealIntent &#61; (Intent) temp;//这是原始的Intent,存起来,后面用得着

intentIndex &#61; i;                        break;

}

}                //构造自己的Intent&#xff0c;这是为了绕过manifest检测(这个Intent是伪造的&#xff01;只是为了让通过manifest检测)

Intent proxyIntent &#61; new Intent();

ComponentName componentName &#61; new ComponentName(packageName, clz);//用ComponentName重新创建一个intent

proxyIntent.setComponent(componentName);

proxyIntent.putExtra(ORI_INTENT_TAG, currentRealIntent);//将真正的proxy作为参数&#xff0c;存放到extras中&#xff0c;后面会拿出来还原

args[intentIndex] &#61; proxyIntent;//替换掉intent

//哟&#xff0c;已经成功绕过了manifest清单检测. 那么&#xff0c;我不能老让它跳到 伪装的Activity啊&#xff0c;我要给他还原回去&#xff0c;那么&#xff0c;去哪里还原呢&#xff1f;

//继续看源码。

}            return method.invoke(amsObj, args);

}

}真正要跳转之前&#xff0c;把原始的Intent还原回去

PS: 这里hook mh的手段&#xff0c;并不是针对 mh本身做代理&#xff0c;而是对mh的mCallback成员.

因为&#xff1a;public class Handler {

...public void dispatchMessage(Message msg) {        if (msg.callback !&#61; null) {

handleCallback(msg);

} else {            if (mCallback !&#61; null) {                if (mCallback.handleMessage(msg)) {                    return;

}

}

handleMessage(msg);

}

}

}handler的dispatchMessage逻辑&#xff0c;是 先执行mCallback的handlerMessage&#xff0c;然后根据它的返回值决定要不要执行handler本身的handlerMessage函数.

我们的目的是还原Intent&#xff0c;并不需要对ActivityThread原本的mH做出逻辑修改&#xff0c;所以&#xff0c;hook mCallback&#xff0c;加入还原Intent的逻辑&#xff0c;即可.这次hook&#xff0c;对照的源码是(源码太长了&#xff0c;我就直接截取了ActivityThread里面一些关键的代码):

image.png

下面是Hook Mh的完整代码://下面进行ActivityThread的mH的hook,这是针对SDK28做的hook

private static void hookActivityThread_mH_After28() {        try {            //确定hook点&#xff0c;ActivityThread类的mh

// 先拿到ActivityThread

Class> ActivityThreadClz &#61; Class.forName("android.app.ActivityThread");

Field field &#61; ActivityThreadClz.getDeclaredField("sCurrentActivityThread");

field.setAccessible(true);

Object ActivityThreadObj &#61; field.get(null);//OK&#xff0c;拿到主线程实例

//现在拿mH

Field mHField &#61; ActivityThreadClz.getDeclaredField("mH");

mHField.setAccessible(true);

Handler mHObj &#61; (Handler) mHField.get(ActivityThreadObj);//ok&#xff0c;当前的mH拿到了

//再拿它的mCallback成员

Field mCallbackField &#61; Handler.class.getDeclaredField("mCallback");

mCallbackField.setAccessible(true);            //2.现在&#xff0c;造一个代理mH&#xff0c;

// 他就是一个简单的Handler子类

ProxyHandlerCallback proxyMHCallback &#61; new ProxyHandlerCallback();//错&#xff0c;不需要重写全部mH&#xff0c;只需要对mH的callback进行重新定义

//3.替换

//将Handler的mCallback成员&#xff0c;替换成创建出来的代理HandlerCallback

mCallbackField.set(mHObj, proxyMHCallback);

} catch (Exception e) {

e.printStackTrace();

}

}    private static class ProxyHandlerCallback implements Handler.Callback {        private int EXECUTE_TRANSACTION &#61; 159;//这个值&#xff0c;是android.app.ActivityThread的内部类H 中定义的常量EXECUTE_TRANSACTION

&#64;Override

public boolean handleMessage(Message msg) {            boolean result &#61; false;//返回值&#xff0c;请看Handler的源码&#xff0c;dispatchMessage就会懂了

//Handler的dispatchMessage有3个callback优先级&#xff0c;首先是msg自带的callback&#xff0c;其次是Handler的成员mCallback,最后才是Handler类自身的handlerMessage方法,

//它成员mCallback.handleMessage的返回值为true&#xff0c;则不会继续往下执行 Handler.handlerMessage

//我们这里只是要hook&#xff0c;插入逻辑&#xff0c;所以必须返回false&#xff0c;让Handler原本的handlerMessage能够执行.

if (msg.what &#61;&#61; EXECUTE_TRANSACTION) {//这是跳转的时候,要对intent进行还原

try {                    //先把相关&#64;hide的类都建好

Class> ClientTransactionClz &#61; Class.forName("android.app.servertransaction.ClientTransaction");

Class> LaunchActivityItemClz &#61; Class.forName("android.app.servertransaction.LaunchActivityItem");

Field mActivityCallbacksField &#61; ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成员

mActivityCallbacksField.setAccessible(true);                    //类型判定&#xff0c;好习惯

if (!ClientTransactionClz.isInstance(msg.obj)) return true;

Object mActivityCallbacksObj &#61; mActivityCallbacksField.get(msg.obj);//根据源码&#xff0c;在这个分支里面,msg.obj就是 ClientTransaction类型,所以&#xff0c;直接用

//拿到了ClientTransaction的List mActivityCallbacks;

List list &#61; (List) mActivityCallbacksObj;                    if (list.size() &#61;&#61; 0) return true;

Object LaunchActivityItemObj &#61; list.get(0);//所以这里直接就拿到第一个就好了

if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true;                    //这里必须判定 LaunchActivityItemClz&#xff0c;

// 因为 最初的ActivityResultItem传进去之后都被转化成了这LaunchActivityItemClz的实例

Field mIntentField &#61; LaunchActivityItemClz.getDeclaredField("mIntent");

mIntentField.setAccessible(true);

Intent mIntent &#61; (Intent) mIntentField.get(LaunchActivityItemObj);

Intent oriIntent &#61; (Intent) mIntent.getExtras().get(ORI_INTENT_TAG);                    //那么现在有了最原始的intent&#xff0c;应该怎么处理呢&#xff1f;

Log.d("1", "2");

mIntentField.set(LaunchActivityItemObj, oriIntent);                    return result;

} catch (Exception e) {

e.printStackTrace();

}

}            return result;

}

}PS:这里有个坑(请看上面 if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true;, 我为什么要加这个判断&#xff1f;因为&#xff0c;我通过debug&#xff0c;发现&#xff0c;从mH里面的msg.what得到的ClientTransaction&#xff0c;它有这么一个成员List mActivityCallbacks; 注意看&#xff0c;从list里面拿到的ClientTransactionItem 的实际类型是&#xff1a;LaunchActivityItem.

)

之前我索引源码的时候&#xff0c;追查Intent的去向&#xff0c;只知道它最后被封装成了一个ClientTransaction

image.png

image.png

image.png

但是&#xff0c;最后我从mH的 switch case EXECUTE_TRANSACTION分支&#xff0c;去debug(因为无法继续往下查源码)的时候&#xff0c;

发现 原本塞进去的ActivityResultItem的   list&#xff0c;居然变成了LaunchActivityItem 的list&#xff0c;而我居然查了半天&#xff0c;查不到是在源码何处发生的变化.

而 LaunchActivityItem 和 ActivityResultItem 他们两个都是ClientTransaction的子类public class LaunchActivityItem extends ClientTransactionItem public class ActivityResultItem extends ClientTransactionItem

emmmm...也是很尴尬。&#61;_ &#61;!

不过&#xff0c;最后能够确定&#xff0c;从mH的 switch case EXECUTE_TRANSACTION分支得到的transaction&#xff0c;就是包含了Intent的包装对象&#xff0c;所以只需要解析这个对象,就可以拿到intent&#xff0c;进行还原.

OK&#xff0c;大功告成&#xff0c;安装好 android 9.0 SDK 28的模拟器&#xff0c;启动起来&#xff0c;运行程序&#xff0c;看看能不能无清单跳转&#xff1a;

结果&#xff0c;脸一黑&#xff1a;报错!***

一份大礼&#xff1a;2019-02-27 18:20:12.287 28253-28253/study.hank.com.activityhookdemo E/AndroidRuntime: FATAL EXCEPTION: main

Process: study.hank.com.activityhookdemo, PID: 28253

java.lang.RuntimeException: Unable to start activity ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222)

at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)

at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61)

at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)

at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14)

at android.app.Activity.performCreate(Activity.java:7136)

at android.app.Activity.performCreate(Activity.java:7127)

at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:435)

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:240)

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:219)

at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)

at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:61)

at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)

at study.hank.com.activityhookdemo.methodA.Main2Activity.onCreate(Main2Activity.java:14)

at android.app.Activity.performCreate(Activity.java:7136)

at android.app.Activity.performCreate(Activity.java:7127)

at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)

at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)

at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)

at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)

at android.os.Handler.dispatchMessage(Handler.java:106)

at android.os.Looper.loop(Looper.java:193)

at android.app.ActivityThread.main(ActivityThread.java:6669)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

提取关键信息&#xff1a;Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{study.hank.com.activityhookdemo/study.hank.com.activityhookdemo.methodA.Main2Activity}

at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222)

居然找不到包&#xff1f;

问题出在&#xff1a;

NavUtils.getParentActivityName还是被谷歌摆了一道&#xff0c;查原因啊&#xff0c;进去NavUtils.getParentActivityName()去看看&#xff1a;

image.png

看来就是这里报的错,继续&#xff1a;

image.png

找到可疑点:

image.png

可能就是这里抛出的异常&#xff0c;继续&#xff1a;

image.png

然而&#xff0c;它是一个接口&#xff0c;那么就找它的实现类(注意&#xff0c;如果这个接口涉及到隐藏&#64;hide的类&#xff0c;你用ctrl&#43;T是不能找到的&#xff0c;不过也有办法&#xff0c;回到NavUtil.java)&#xff1a;

image.png

哦&#xff0c;原来pm对象是来自context&#xff0c;既然提到了context这个抽象类&#xff0c;它的很多抽象方法的实现都在ContextImpl&#xff0c;手动进入ContextImpl:找这个方法&#xff1a;

image.png

这个pm对象原来是来自ActivityThread,然后进行了一次封装&#xff0c;最后返回出去的是一个ApplicationPackageManager对象.

那就进入主线程咯.

image.png

看看IPackageManager的内容&#xff1a;它是AIDL动态生成的接口&#xff0c;用androidStudio是看不到接口内容的&#xff0c;只能到源码官网,查到的接口如下&#xff1a;

interface IPackageManager {

...  ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);

}

Ok&#xff0c;看到IBinder&#xff0c;就知道应该无法继续往下追查了&#xff0c;已经跨进程了.

前面提到了&#xff0c;从主线程拿到的pm&#xff0c;被封装成了ApplicationPackageManager&#xff0c;那么&#xff0c;进入它里面去找:getActivityInfo方法&#xff1a;

image.png

原来异常是这里抛出的&#xff0c;当mPm.getActivityInfo为空的时候&#xff0c;才会抛出.

OK&#xff0c;就查到这里&#xff0c;得出结论:

源码&#xff0c;其实对Activity的合法性进行了两次检测&#xff0c;一次是在AMS&#xff0c;一次是在这里的PMS&#xff0c;前面的AMS&#xff0c;我们用一个已有的Activity伪装了一下&#xff0c;通过了验证&#xff0c;那么这里的PMS&#xff0c;我们也可以采用同样的方式.

注:上图的参数ComponentName className&#xff0c;其实&#xff0c;他就是&#xff01;Intent的ComponentName成员&#xff1a;

image.png

懂了吧··这里对intent又进行了一次检查&#xff0c;检查的就是这个ComponentName.

接下来用同样的方式对PMS的检测进行hook&#xff0c;让它不再报异常.

此次hook的参照的源码是&#xff1a;

image.png

hook核心代码如下(对sPackageManager进行代理替换&#xff0c;让代理类检查的永远是合法的Activity)&#xff1a;private static void hookPMAfter28(Context context) throws ClassNotFoundException,

NoSuchFieldException, IllegalAccessException, NoSuchMethodException,

InvocationTargetException {        String pmName &#61; Util.getPMName(context);        String hostClzName &#61; Util.getHostClzName(context, pmName);

Class> forName &#61; Class.forName("android.app.ActivityThread");//PM居然是来自ActivityThread

Field field &#61; forName.getDeclaredField("sCurrentActivityThread");

field.setAccessible(true);        Object activityThread &#61; field.get(null);

Method getPackageManager &#61; activityThread.getClass().getDeclaredMethod("getPackageManager");        Object iPackageManager &#61; getPackageManager.invoke(activityThread);        String packageName &#61; Util.getPMName(context);

PMSInvocationHandler handler &#61; new PMSInvocationHandler(iPackageManager, packageName, hostClzName);

Class> iPackageManagerIntercept &#61; Class.forName("android.content.pm.IPackageManager");        Object proxy &#61; Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new

Class>[]{iPackageManagerIntercept}, handler);        // 获取 sPackageManager 属性

Field iPackageManagerField &#61; activityThread.getClass().getDeclaredField("sPackageManager");

iPackageManagerField.setAccessible(true);

iPackageManagerField.set(activityThread, proxy);

}    static class PMSInvocationHandler implements InvocationHandler {

private Object base;

private String packageName;

private String hostClzName;

public PMSInvocationHandler(Object base, String packageName, String hostClzName) {            this.packageName &#61; packageName;            this.base &#61; base;            this.hostClzName &#61; hostClzName;

}

&#64;Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            if (method.getName().equals("getActivityInfo")) {

ComponentName componentName &#61; new ComponentName(packageName, hostClzName);                return method.invoke(base, componentName, PackageManager.GET_META_DATA, 0);//破费&#xff0c;一定是这样

}            return method.invoke(base, args);

}

}

4.最终效果ok,见证奇迹的时候到了&#xff0c;准备好SDK28 -android 9.0虚拟机&#xff0c;运行demo&#xff1a;

作者&#xff1a;波澜步惊

链接&#xff1a;https://www.jianshu.com/p/eb772e50c690



推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • JVM的方法执行引擎模板表
    Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化。这一篇首先介绍模板表。在启动虚拟机阶段会调用init_globals()方 ... [详细]
  • 查看全部数据库表参照地址:https:www.cnblogs.comzhoulixiangblogp12078724.html本文所用数据库表:prod_idvend_idprod ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了一个线程多次调用一个函数相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 不知道你是否还记得之前在进程中的信号处理时,提到过阻塞信号集与未决信号集的概念,如果你已经忘记了,请参考《阻塞信号与未决信号》一文回忆一下 ... [详细]
  • linux内核网络钩子函数使用,Linux内核IOCTL网络控制框架实现实例分析
    4.6、inet_ioctl函数由于inet_ioctl函数内容分支很多,但功能、处理不难理解,所以我把一些不常见的内容都省去,挑简单重要的说,完全在于抛砖引玉:staticint ... [详细]
  • iOS安全攻防(二十四):敏感逻辑的保护方案(1)Objective-C代码容易被hook,暴露信息太赤裸裸,为了安全,改用C来写吧!当然不是全部代码都要C来写,我指的是敏感业务逻 ... [详细]
  • pthread_mutex_lockpthread_mutex_lock(pthread_mutex_t*mutex);intpthread_mutex_trylock(pthre ... [详细]
  • C语言编程gcc怎么生成静态库.a和动态库.so
    这篇文章将为大家详细讲解有关C语言编程gcc怎么生成静态库.a和动态库.so,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章 ... [详细]
  • DiceCTF 2021 flippidy
    检查一下保护机制:只有pie没有开ida看下程序逻辑:main函数:第13行获取一个数字,第14行malloc(8*num)申请一个堆块,这个堆块将被用于保存其它堆块的堆址然后进入 ... [详细]
author-avatar
客户服务部小谢娟_387
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有