前言
Android Hook 插件化其实已经不是什么新鲜的技术了,不知你有没有想过,支付宝中那么多小软件:淘票票 ,火车票等软件,难道是支付宝这个软件自己编写的吗?那不得写个十年,软件达到几十G,但是并没有,玩游戏时那么多的皮肤包肯定时用户使用哪个就下载哪个皮肤包。
一 未在配置文件中注册的Activity可以启动吗?
从0学的时候就知道Activity必须在配置软件库文件中注册android电子市场,否则无法启动且报错。但是Hook告诉你的是,android是什么系统未在配置文件中注册Activandroid电子市场ity是可以启动的,惊不惊喜?意不意外?
通过本文你可以学到:
1.通过对startActivity方法apple苹果官网进行Hook,实现为startActivity方法添加日志。
1.1 通过对Instrumentation进行Hook
1.2 通过对AMNandroid下载进行Hook
2.如何启动一个未在配置文件中注册的Activity实现插件化
本片文章基础建立在 Java反射机制和App启动流程解析,软件管家建议不太了解的小伙伴可以先移步至这两篇文章。
二对android是什么系统startActivity方法进行Hook
通过对查阅startActivity的源码可以看出standroid是什么系统artActivity最终都会走到startActivityFoResult方法中
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if(this.mParent == null) {
ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);
if(ar != null) {
this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
}
if(requestCode >= 0) {
this.mStartedActivity = true;
}
} else if(options != null) {
this.mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
this.mParent.startActivityFromChild(this, intent, requestCode);
}
}
通过mInapp小胖子strumenta变量泵tion.execStartActivity调用(ps:详细android是什么手机牌子的源码解析已在上篇文章中讲解),再看mInstrumentation.execStartActivity方法变量是什么意思源码如下:
public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread)contextThread;
if(this.mActivityMonitors != null) {
Object e = this.mSync;
synchronized(this.mSync) {
int N = this.mActivityMonitors.size();
for(int i = 0; i < N; ++i) {
Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);
if(am.match(who, (Activity)null, intent)) {
++am.mHits;
if(am.isBlocking()) {
return requestCode >= 0?am.getResult():null;
}
break;
}
}
}
}
try {
intent.setAllowFds(false);
intent.migrateExtraStreamToClipData();
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
checkStartActivityResult(var16, intent);
} catch (RemoteException var14) {
;
}
return null;
}
最终会交给 int var1android是什么系统6 = ActivityManagerNative.g变量4etD软件库e软件管家fault().startActivity(软件技术专业whoT软件技术专业hread, intent,…处理,所以如果我们想对startActivity方法进行Hook,可以从这两个地方入手(其实不止这两个地方,我们只讲解着两个地方,下面使用的反射封装类也在上篇文章中给出)。
- 2.1 对mIandroid下载nstrumentation进行Hook
在Activity.class类中定义了私有变量
private Instrumentation mInstrumentation;
我们首先要做的就是修改这个私有变量的值,在执行方法前打印一行日志,首先我们通过反射来获取这一私有变量。
Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");
我们要做的是将这个Instrumentatandroid什么意思ion替换成我们自己的Instrumentation,所以下面我们新建MyInstrumentation继承自Instrandroid是什么系统umentation,并且MyInstrumentation的execStartActivity方法不变。
public class MyInstrumentation extends Instrumentation {
private Instrumentation instrumentation;
public MyInstrumentation(Instrumentation instrumentation) {
this.instrumentation = instrumentation;
}
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
Log.d("-----","啦啦啦我是hook进来的!");
Class[] classes = {Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class,Bundle.class};
Object[] objects = {who,contextThread,token,target,intent,requestCode,options};
Log.d("-----","啦啦啦我是hook进来的!!");
return (ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects);
}
我们直接通过反射调用这个方法 参数就是 Class[] classes = {Context.class,IBinder.cAPPlass,IBinder.class,Activity.class,Intent.class,int.class,变量Bundle.class}与方法名中一致
(ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects)
如果android下载我们这里不调用它本身的execStartActivity方法的话,那么s变量tartAandroid下载ctivity就无效了。
然后我们将自定义的替换为原来的Instappstorerumentation
Reflex.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);
完整代码就是apple苹果官网
Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");
MyInstrumentation instrumentation1 = new MyInstrumentation(instrumentation);
Reflex.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);
运行日志如下:
这个时候我们就APP成功的Hook了startActivity方法
2.2 对AMN进行Hook
execStartActivity方Android法最终会走到ActivityMan软件技术专业agerNative.getDefault().AndroidstartActivity方法
try {
intent.setAllowFds(false);
intent.migrateExtraStreamToClipData();
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
checkStartActivityResult(var16, intent);
} catch (RemoteException var14) {
;
}
你可能会说上面说过了啊,替换ActivityManagerNative.getDefault(),重写startA变量的指针其含义是指该变量的ctivity方法,我们来看ActivityManagerNative.getDefault()
public static IActivityManager getDefault() {
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类,在AmsHookHelperUtilsandroid平板电脑价格类中处理反射代码
gDefault是个finaandroid是什么手机牌子l静态类型的字段,首先我们获取gDefault字段
Object gDefault = Reflex.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
gDefault是Singleton<IActivityManager>类型的对象,Singleton是一个单例模式
public abstract class Singleton<T> {
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));
我们的代理对象就是napple苹果官网ew AMNInvocationHanlder(mInstanceappearance),(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);
}
}
所有的代理类都要Android实现InvocationHandler接口,android/yunos在invoke方法中method.invoke(target,args);表示的就是 执行被代理对象所对应的方法。appstore因为AMN Singleton做的事情比较多,所以这里只对star软件tActivity方法hook
if (method.getName().equals(actionName)){
Log.d("---","啦啦啦我是hook AMN进来的");
return method.invoke(target,args);
}
然后我们将gDefault字段替换为我们的代理类
Reflex.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);
AmsHookHe变量与函数lperUtils方android下载法整体如下:
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的getD软件测试efault方法。
2.3 如何启动一android是什么手机牌子个未注册的Activity
如何启动一个未注册的Activity,首先我们了解Activity的启动流程,App的启动流程已经在上篇文章中讲解了,APP启动流程解析,还不了解的小伙伴appstore,可先移步至上篇文章。假设现在MainActivity,Main2Activity,Mappointmentain3Activity,其中Main3Activity未注册,我们在MainActivity中启动Main3Activity,当启app小胖子动Main3Actandroid手机ivi变量4ty的时候,AMS会在配置文件中检查,是否有Main3Activity的配置信息如果不存在则报错软件,存在则启动Main3Activity。
所以我们可以做的是,将要启动的Activity发送给AMS之前,将要启动的Activity替换未已经注册Activity Main2Activity,这样AMS就可以检验通过,当AMS要启动目标Activity软件技术专业的时候再将Main2Activiandroid下载安装ty替换为真正要启动的Activity。
首先我们按照上面逻辑先对startActivity方法进行Hook,这里采用对AMN Hook的方式。和上述代反射机制码一样,不一样的地方在于mInstance的代理类不同。
新建一个AMNInvocationHanlder1对象同样继承自InvocationHandler,只拦截startActivity方法。
if (method.getName().equals(actionName)){}
在这里我们要做的就是将要启动的Main3Activity替换为Mandroid是什么系统ain2Activity,这样能绕过A软件管家MS的检验,首先我android平板电脑价格们从目标方法中取出目标Aandroid下载安装ctivity。
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);
表示会执行原本的方法,而我们软件技术专业来看原本的staappointmentrtActivity方法如下:
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就被替appear换成了Main2Activity,不过这个冒充者还要将原本的目标携带过去,等待真正打开的时候再替换回来,否则就真的启动这个冒充android平板电脑价格者了
newIntent.putExtra(AmsHookHelperUtils.TUREINTENT,intent);
这个时候我们调用这个方法什么都不做,这个时候启动Main3Activity
startActivity(new Intent(this,Main3Activity.class));
显示的其实是Main2Activity,如图变量与函数所示:
这样说明我们的冒充者已经成功替换了真实目标,所以我们接下来要在启动的时候,将冒充软件测试者再重新替换为目标android/yunos者,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收到消息后进行处理
publicvoidhandleMessage(Messagemsg){
ActivityThread.ActivityClientRecorddata;
switch(msg.what){
case100:
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是Ha变量类型有哪些ndler类软件库型的消息处理类,所以sendMessage方法会调用callback,Handler消息处理机制可看我之前一篇博客
深入理解Android消息机制,所以我们可以对callback字段进行Hook。如果不明白怎么办?软件工程关注我!
新建hookActiviappearancetyThread方法,首先我们获取当前的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方法application
@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");
然后从目标对象中取出携带过来的真实对象,并将in变量4tent修改为真实目标对象的信息,这样就可以启动真实的目标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());
}
}
这个时候再启动未注册的Main3Activandroid下载安装ity,appearance就可以成功启动了
startActivity(new Intent(this,Main3Activity.class));
这样我们就成功的启动了未注册Activity