使用场景
例如一些商城app,在没有注册的情况下可以浏览商品但是在加入购物车或别的操作下会跳转到注册登录界面,等到注册登录后又会返回到购物车界面,对于这种需求就可以使用hook技术实现。
技术点分析
只需要hook两个地方,1.hook startActivity方法绕过AMS检查,将未注册的Activity伪装成注册的activity ,使用动态代理的形式创建IActiviytManager的代理类替换系统的IActvityManager 2.launchActity时会在ActivityThread的Handler处理这个消息(100),hook 这个handler,重写handlemessage方法,获取实际需要启动的intent。
详细请看HookUtil
public class HookUtil {
private Context context;
public void hookHookMh(Context context ) {
try {
Class<?> forName = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = forName.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
// 还原系统的ActivityTread mH
Object activityThreadObj=currentActivityThreadField.get(null);
Field handlerField = forName.getDeclaredField("mH");
handlerField.setAccessible(true);
// hook点找到了
Handler mH= (Handler) handlerField.get(activityThreadObj);
Field callbackField = Handler.class.getDeclaredField("mCallback");
callbackField.setAccessible(true);
callbackField.set(mH,new ActivityMH(mH));
} catch (Exception e) {
e.printStackTrace();
}
}
public void hookStartActivity(Context context) {
// 还原 gDefault 成员变量 反射 调用一次
this.context = context;
try {
Class<?> ActivityManagerNativecls=Class.forName("android.app.ActivityManagerNative");
Field gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
gDefault.setAccessible(true);
// 因为是静态变量 所以获取的到的是系统值 hook 伪hook
Object defaltValue=gDefault.get(null);
//mInstance对象
Class<?> SingletonClass=Class.forName("android.util.Singleton");
Field mInstance = SingletonClass.getDeclaredField("mInstance");
// 还原 IactivityManager对象 系统对象
mInstance.setAccessible(true);
Object iActivityManagerObject=mInstance.get(defaltValue);
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
startActivty startActivtyMethod = new startActivty(iActivityManagerObject);
// 第二参数 是即将返回的对象 需要实现那些接口
Object oldIactivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
, new Class[]{IActivityManagerIntercept, View.OnClickListener.class}
, startActivtyMethod);
// 将系统的iActivityManager 替换成 自己通过动态代理实现的对象 oldIactivityManager对象 实现了 IActivityManager这个接口的所有方法
mInstance.set(defaltValue, oldIactivityManager);
} catch (Exception e) {
e.printStackTrace();
}
}
class ActivityMH implements Handler.Callback{
private Handler mH;
public ActivityMH(Handler mH) {
this.mH = mH;
}
@Override
public boolean handleMessage(Message msg) {
//LAUNCH_ACTIVITY ==100 即将要加载一个activity了
if (msg.what == 100) {
//加工 --完 一定丢给系统 secondActivity -hook->proxyActivity---hook-> secondeActivtiy
handleLuachActivity(msg);
}
//做了真正的跳转
mH.handleMessage(msg);
return true;
}
private void handleLuachActivity(Message msg) {
// 还原
Object obj = msg.obj;
try {
Field intentField=obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
// ProxyActivity 2
Intent realyIntent = (Intent) intentField.get(obj);
// sconedActivity 1
Intent oldIntent = realyIntent.getParcelableExtra("oldIntent");
if (oldIntent != null) {
// 集中式登录
SharedPreferences share = context.getSharedPreferences("sp_info",
Context.MODE_PRIVATE);
if (share.getBoolean("login",false)||oldIntent.getComponent().getClassName().equals(SceondActivity.class.getName())) {
// 登录 还原 把原有的意图 放到realyIntent
realyIntent.setComponent(oldIntent.getComponent());
}else {
ComponentName componentName = new ComponentName(context,LoginActivity.class);
realyIntent.putExtra("extraIntent", oldIntent.getComponent()
.getClassName());
realyIntent.setComponent(componentName);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class startActivty implements InvocationHandler {
private Object iActivityManagerObject;
public startActivty(Object iActivityManagerObject) {
this.iActivityManagerObject = iActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i("INFO","invoke "+method.getName());
if ("startActivity".equals(method.getName())) {
Log.i("INFO","-----------------startActivity--------------------------");
//瞒天过海
// 寻找传进来的intent
Intent intent = null;
int index=0;
for (int i=0;i<args.length;i++) {
// intent
Object arg = args[i];
if (arg instanceof Intent) {
intent = (Intent) args[i];
index = i;
}
}
//目的 ---载入acgtivity 将它还原
Intent newIntent = new Intent();
ComponentName componentName = new ComponentName(context, ProxyActivity.class);
newIntent.setComponent(componentName);
// 真实的意图 被我隐藏到了 键值对
newIntent.putExtra("oldIntent", intent);
args[index] = newIntent;
}
return method.invoke(iActivityManagerObject, args);
}
}
}
清单文件中只需要生命下代理activity 其他的activity都不用申明
<activity android:name=".ProxyActivity"/>
登录成功后修改intent的意图,跳转到实际需要跳转的类
public class LoginActivity extends Activity {
EditText name;
EditText password;
private String className;
SharedPreferences share;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
name = (EditText) findViewById(R.id.name);
password = (EditText) findViewById(R.id.password);
share = this.getSharedPreferences("sp_info", MODE_PRIVATE);//实例化
className = getIntent().getStringExtra("extraIntent");
if (className != null) {
((TextView)findViewById(R.id.text)).setText(" 跳转界面:"+className);
}
}
public void login(View view) {
if ((name.getText() == null || password.getText() == null)) {
Toast.makeText(this, "请填写用户名 或密码",Toast.LENGTH_SHORT).show();
return;
}
if ("david".equals(name.getText().toString()) && "123456".equals(password.getText()
.toString())) {
SharedPreferences share = super.getSharedPreferences("sp_info", MODE_PRIVATE);//实例化
SharedPreferences.Editor editor = share.edit(); //使处于可编辑状态
editor.putString("name", name.getText().toString());
editor.putString("sex", password.getText().toString());
editor.putBoolean("login",true); //设置保存的数据
Toast.makeText(this, "登录成功",Toast.LENGTH_SHORT).show();
editor.commit(); //提交数据保存
if (className != null) {
//跳转到本来要调整的activity
ComponentName componentName = new ComponentName(this, className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
finish();
}
}else{
SharedPreferences.Editor editor = share.edit(); //使处于可编辑状态
editor.putBoolean("login",false); //设置保存的数据
Toast.makeText(this, "登录失败",Toast.LENGTH_SHORT).show();
editor.commit(); //提交数据保存
}
}
}
在application中注册hook
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HookUtil hookUtil = new HookUtil();
hookUtil.hookStartActivity(this);
hookUtil.hookHookMh(this);
}
}