return (IActivityManager)gDefault.get();
}
public final T get() {
synchronized(this) {
if(this.mInstance == null) {
this.mInstance = this.create();
}
return this.mInstance;
}
}
可以看出IActivityManager是一个接口,gDefault.get()返回的是一个泛型,上述方案我们无法入手,所以我们这里要用动态代理方案
我们定义一个AmsHookHelperUtils类,在AmsHookHelperUtils类中处理反射代码
gDefault是个final静态类型的字段,首先我们获取gDefault字段
Object gDefault = Reflex.getStaticFieldObject(“android.app.ActivityManagerNative”,“gDefault”);
gDefault是 Singleton类型的对象,Singleton是一个单例模式
public abstract class Singleton {
private T mInstance;
public Singleton() {
}
protected abstract T create();
public final T get() {
synchronized(this) {
if(this.mInstance == null) {
this.mInstance = this.create();
}
return this.mInstance;
}
}
}
接下里我们来取出mInstance字段
Object mInstance = Reflex.getFieldObject(“android.util.Singleton”,gDefault,“mInstance”);
然后创建一个代理对象
Class<?> classInterface = Class.forName(“android.app.IActivityManager”);
Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),
new Class<?>[]{classInterface},new AMNInvocationHanlder(mInstance));
我们的代理对象就是new AMNInvocationHanlder(mInstance),(ps:代理模式分为静态代理和动态代理,如果对代理模式不了解可以百度一波,也可以关注我,等待我的代理模式相关文章)
public class AMNInvocationHanlder implements InvocationHandler {
private String actionName = “startActivity”;
private Object target;
public AMNInvocationHanlder(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals(actionName)){
Log.d(“—”,“啦啦啦我是hook AMN进来的”);
return method.invoke(target,args);
}
return method.invoke(target,args);
}
}
所有的代理类都要实现InvocationHandler接口,在invoke方法中method.invoke(target,args);表示的就是 执行被代理对象所对应的方法。因为AMN Singleton做的事情比较多,所以这里只对startActivity方法hook
if (method.getName().equals(actionName)){
Log.d(“—”,“啦啦啦我是hook AMN进来的”);
return method.invoke(target,args);
}
然后我们将gDefault字段替换为我们的代理类
Reflex.setFieldObject(“android.util.Singleton”,gDefault,“mInstance”,proxy);
AmsHookHelperUtils方法整体如下:
public class AmsHookHelperUtils {
public static void hookAmn() throws ClassNotFoundException {
Object gDefault = Reflex.getStaticFieldObject(“android.app.ActivityManagerNative”,“gDefault”);
Object mInstance = Reflex.getFieldObject(“android.util.Singleton”,gDefault,“mInstance”);
Class<?> classInterface = Class.forName(“android.app.IActivityManager”);
Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),
new Class<?>[]{classInterface},new AMNInvocationHanlder(mInstance));
Reflex.setFieldObject(“android.util.Singleton”,gDefault,“mInstance”,proxy);
}
}
我们调用AmsHookHelperUtils.hookAmn();然后启动一个新的Activity,运行日志如下:
这样我们就成功Hook了AMN的getDefault方法。
2.3 如何启动一个未注册的Activity
如何启动一个未注册的Activity,首先我们了解Activity的启动流程,App的启动流程已经在上篇文章中讲解了,APP启动流程解析,还不了解的小伙伴,可先移步至上篇文章。假设现在MainActivity,Main2Activity,Main3Activity,其中Main3Activity未注册,我们在MainActivity中启动Main3Activity,当启动Main3Activity的时候,AMS会在配置文件中检查,是否有Main3Activity的配置信息如果不存在则报错,存在则启动Main3Activity。
所以我们可以做的是,将要启动的Activity发送给AMS之前,将要启动的Activity替换未已经注册Activity Main2Activity,这样AMS就可以检验通过,当AMS要启动目标Activity的时候再将Main2Activity替换为真正要启动的Activity。
首先我们按照上面逻辑先对startActivity方法进行Hook,这里采用对AMN Hook的方式。和上述代码一样,不一样的地方在于mInstance的代理类不同。
新建一个AMNInvocationHanlder1对象同样继承自InvocationHandler,只拦截startActivity方法。
if (method.getName().equals(actionName)){}
在这里我们要做的就是将要启动的Main3Activity替换为Main2Activity,这样能绕过AMS的检验,首先我们从目标方法中取出目标Activity。
Intent intent;
int index = 0;
for (int i = 0;i<args.length;i++){
if (args[i] instanceof Intent){
index = i;
break;
}
}
你可能会问你怎么知道args中一定有intent类的参数,因为invoke方法中最终会执行
return method.invoke(target,args);
表示会执行原本的方法,而我们来看原本的startActivity方法如下:
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
所以我们说args中肯定有个intent类型的参数,获取真实目标Activity之后,我们获取目标的包名
intent = (Intent) args[index];
String packageName = intent.getComponent().getPackageName();
新建一个Intent 将intent设置为 冒充者Main2Activity的相关信息
Intent newIntent = new Intent();
ComponentName componentName = new ComponentName(packageName,Main2Activity.class.getName());
newIntent.setComponent(componentName);
args[index] = newIntent;
这样目标Activity就被替换成了Main2Activity,不过这个冒充者还要将原本的目标携带过去,等待真正打开的时候再替换回来,否则就真的启动这个冒充者了
newIntent.putExtra(AmsHookHelperUtils.TUREINTENT,intent);
这个时候我们调用这个方法什么都不做,这个时候启动Main3Activity
startActivity(new Intent(this,Main3Activity.class));
显示的其实是Main2Activity,如图所示:
这样说明我们的冒充者已经成功替换了真实目标,所以我们接下来要在启动的时候,将冒充者再重新替换为目标者,ActivityThread通过mH发消息给AMS
synchronized(this) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
this.mH.sendMessage(msg);
}
AMS收到消息后进行处理
public void handleMessage(Message msg) {
ActivityThread.ActivityClientRecord data;
switch(msg.what) {
case 100:
Trace.traceBegin(64L, “activityStart”);
data = (ActivityThread.ActivityClientRecord)msg.obj;
data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);
ActivityThread.this.handleLaunchActivity(data, (Intent)null);
Trace.traceEnd(64L);
mH是Handler类型的消息处理类,所以sendMessage方法会调用callback,Handler消息处理机制可看我之前一篇博客
深入理解Android消息机制,所以我们可以对callback字段进行Hook。如果不明白怎么办?关注我!
新建hookActivityThread方法,首先我们获取当前的ActivityThread对象
Object currentActivityThread = Reflex.getStaticFieldObject(“android.app.ActivityThread”, “sCurrentActivityThread”);
然后获取对象的mH对象
Handler mH = (Handler) Reflex.getFieldObject(currentActivityThread, “mH”);
将mH替换为我们的自己自定义的MyCallback。
Reflex.setFieldObject(Handler.class, mH, “mCallback”, new MyCallback(mH));
自定义MyCallback首先 Handler.Callback接口,重新处理handleMessage方法
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 100:
handleLaunchActivity(msg);
break;
default:
break;
}
mBase.handleMessage(msg);
return true;
}
我们获取传递过来的目标对象
Object obj = msg.obj;
Intent intent = (Intent) Reflex.getFieldObject(obj, “intent”);
然后从目标对象中取出携带过来的真实对象,并将intent修改为真实目标对象的信息,这样就可以启动真实的目标Activity
Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);
intent.setComponent(targetIntent.getComponent());
MyCallbackt如下
**
- Created by Huanglinqing on 2019/4/30.
*/
public class MyCallback implements Handler.Callback {
Handler mBase;
public MyCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 100:
handleLaunchActivity(msg);
break;
default:
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
Object obj = msg.obj;
Intent intent = (Intent) Reflex.getFieldObject(obj, “intent”);
Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);
intent.setComponent(targetIntent.getComponent());
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
结语
看到这篇文章的人不知道有多少是和我一样的Android程序员。
35岁,这是我们这个行业普遍的失业高发阶段,这种情况下如果还不提升自己的技能,进阶发展,我想,很可能就是本行业的职业生涯的终点了。
我们要有危机意识,切莫等到一切都成定局时才开始追悔莫及。只要有规划的,有系统地学习,进阶提升自己并不难,给自己多充一点电,你才能走的更远。
千里之行始于足下。这是上小学时,那种一元钱一个的日记本上每一页下面都印刷有的一句话,当时只觉得这句话很短,后来渐渐长大才慢慢明白这句话的真正的含义。
有了学习的想法就赶快行动起来吧,不要被其他的事情牵绊住了前行的脚步。不要等到裁员时才开始担忧,不要等到面试前一晚才开始紧张,不要等到35岁甚至更晚才开始想起来要学习要进阶。
给大家一份系统的Android学习进阶资料,希望这份资料可以给大家提供帮助。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
结语
看到这篇文章的人不知道有多少是和我一样的Android程序员。
35岁,这是我们这个行业普遍的失业高发阶段,这种情况下如果还不提升自己的技能,进阶发展,我想,很可能就是本行业的职业生涯的终点了。
我们要有危机意识,切莫等到一切都成定局时才开始追悔莫及。只要有规划的,有系统地学习,进阶提升自己并不难,给自己多充一点电,你才能走的更远。
千里之行始于足下。这是上小学时,那种一元钱一个的日记本上每一页下面都印刷有的一句话,当时只觉得这句话很短,后来渐渐长大才慢慢明白这句话的真正的含义。
有了学习的想法就赶快行动起来吧,不要被其他的事情牵绊住了前行的脚步。不要等到裁员时才开始担忧,不要等到面试前一晚才开始紧张,不要等到35岁甚至更晚才开始想起来要学习要进阶。
给大家一份系统的Android学习进阶资料,希望这份资料可以给大家提供帮助。
[外链图片转存中…(img-XhKs6rCz-1712337668376)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!