首先关于startActivity 我们平时会经常使用到 在activity内 直接startActivity(intent)
其实这里还有一种start方式 我们可能没怎么用过 getApplicationContext.startActivity(intent) 通过这种方式可以开启其它程序中的activity

  • 代理
    在说Hook之前先说下代理包括静态代理和动态代理如果不熟悉的可以看下
http://www.jianshu.com/p/8507b4364f11
  • 反射
    其次就是java反射方面的知识
    如何通过变量获取实例对象再进行替换

Hook:我们把修改参数,替换返回值,称之为Hook
Hook点:静态变量和单利;即就是不容易发生改变的对象,容易定位 普通的对象容易发生改变 所以我们可以根据这个原则去hook

1. 首先我们分析下我们平时直接使用的startActivity()方法

源码

@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
@Override
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}

最终调用了startActivityForResult()方法 在方法内调用了mInstrumentation变量的execStartActivity()方法 所以最终startActivity的是Instrumentation类的execStartActivity()方法(nativie层面的)其实是通过远端的ActivityManagerService启动的 是一个跨进程通信的过程

public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}

这里呢 我们可以去hook掉activity类内变量 mInstrumentation 将它替换成我们自己的Instrumentation
通过手写静态代理类,替换掉原始的方法

public class EvilInstrumentation extends Instrumentation {

    private static final String TAG = "EvilInstrumentation";

    // ActivityThread中原始的对象, 保存起来
Instrumentation mBase; public EvilInstrumentation(Instrumentation base) {
mBase = base;
} public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) { // Hook之前, XXX到此一游!
Log.e(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
"\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
"\ntarget = [" + target + "], \nintent = [" + intent +
"], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");
Uri uri = Uri.parse("http://www.jianshu.com/u/3d228ed8d54d");
Intent intent1 = new Intent(Intent.ACTION_VIEW,uri);
// 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了.
// 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
try { //这里通过反射找到原始Instrumentation类的execStartActivity方法
Method execStartActivity = Instrumentation.class.getDeclaredMethod(
"execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
execStartActivity.setAccessible(true); //执行原始Instrumentation的execStartActivity方法 再次之前 you can do whatever you want ...
return (ActivityResult) execStartActivity.invoke(mBase, who,
contextThread, token, target, intent1, requestCode, options);
} catch (Exception e) {
// 某该死的rom修改了 需要手动适配
throw new RuntimeException("do not support!!! pls adapt it");
}
}
}

在这里我们可以拿到Intent 当然你可以将这个intent对象进行替换 跳转到另一个界面(whatever you want)

有了代理对象 下面使用反射直接进行替换

public static void replaceInstrumentation(Activity activity){
Class<?> k = Activity.class;
try {
//通过Activity.class 拿到 mInstrumentation字段
Field field = k.getDeclaredField("mInstrumentation");
field.setAccessible(true);
//根据activity内mInstrumentation字段 获取Instrumentation对象
Instrumentation instrumentation = (Instrumentation)field.get(activity);
//创建代理对象
Instrumentation instrumentationProxy = new EvilInstrumentation(instrumentation);
//进行替换
field.set(activity,instrumentationProxy);
} catch (IllegalAccessException e){
e.printStackTrace();
}catch (NoSuchFieldException e){
e.printStackTrace();
} }

有注释不用过多解释了

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HookActivityHelper.replaceInstrumentation(this);
Button tv = new Button(this);
tv.setText("测试界面");
setContentView(tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
});
}

测试当我们点击button后 跳转到了自己的简书主页而不是百度 同时看到log

 
image.png

可见 我们确实hook成功了

2.下面使用 getApplicationContext().startActivity(intent)这种方式开启另一个activity

Context开启activity 实质为 ContextImpl(Context的实现类)去真正开启的

final ActivityThread mMainThread;
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}

可以看到是调用了ActivityThread类的getInstrumentation()方法返回mInstrumentation变量后 再调用execStartActivity()方法

Instrumentation mInstrumentation;
public Instrumentation getInstrumentation()
{
return mInstrumentation;
}

整个流程 almost like this

 
未命名文件.png

所以现在变成了我们需要替换ActivityThread类(不是主线程类 final类只不过运行在主线程而已)中的mInstrumentation,首先我们需要拿到ActivityThread对象吧 刚好在ActivityThread类中有个静态方法currentActivityThread可以帮助我们拿到这个对象类

private static ActivityThread sCurrentActivityThread;
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}

so 我们的反射代码如下

      // 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod =
activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
//currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数
Object currentActivityThread = currentActivityThreadMethod.invoke(null);

拿到ActivityThread对象之后 后面的就和第一种方法一样了 将ActivityThread类中的mInstrumentaion换为EvilInstrumentation

其实比较这两种方法的区别:

  • Activity的startActivity 是替换 Activity类内部的mInstrumentation
  • Context的startActivity 是替换 ActivityThread类内部的mInstrumentation

还有一点就是拦截的时机
第一种方式在Activity的onCreate()方法内部拦截即可 而第二种方式需要在onCreate()方法之前attachBaseContext()方法中进行拦截
这里就牵扯到一些应用启动后的生命周期:当启动应用时,会孵化一个新进程,启动应用的启动入口为ActivityThread.main(),后面会新建一个ContextImpl实例给application,在后续的启动中,会进行activity的创建,其中ActivityThread会调用performLaunchActivity,先通过createBaseContextForActivity创建Context,然后再activity.attach()

在attach()方法中

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mInstrumentation = instr;
}

我们看到首先调用了attachBaseContext(context)这个方法
然后给成员mInstrumentation赋值
so 如果我们在onCreate()方法中去做hook是hook不到的 时机已晚

@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
Log.e("activity","attachBaseContext");
// 在这里进行Hook
HookHelper.attachContext();
} catch (Exception e) {
e.printStackTrace();
}
}

测试同样ok

3.前两种方法都是替换Instrumentation 这种方法我们看看在Instrumentation类中的execStartActivity()方法

 int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);

真正开启的是这句 ActivityManagerNative.getDefault().startActivity()
首先ActivityManagerNative是个什么鬼 点开后发现

ublic abstract class ActivityManagerNative extends Binder implements IActivityManager

继承了Binder 实现了 IActivityManager 而且是个抽象类 类图如下

 
image.png

这张图得看半天
继续往下走

static public IActivityManager getDefault() {
return gDefault.get();
} private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}

getDefault返回的是一个IActivityManager对象
这里gDefault借助Singleton类实现单利模式 gDefault为static final 形式 根据我们的hook原则 这是一个比较好hook的点

代码如下:

public class HookUtil {
private static final String TAG = "HookUtil";
public void hookIActivityManager() {
try {
Class<?> c = Class.forName("android.app.ActivityManagerNative"); Field field = c.getDeclaredField("gDefault"); field.setAccessible(true); //返回ActivityManagerNative对象
Object amn = field.get(null); Class<?> k = Class.forName("android.util.Singleton"); Field field1 = k.getDeclaredField("mInstance"); field1.setAccessible(true); //拿到ActivityManagerProxy对象 代理ActivityManagerNative对象的子类ActivityManagerService
//为什么不是IActivityManager对象
//因为在gDefault对象的 实现方法 onCreate()方法中 asInterface(b)返回的是 return new ActivityManagerProxy(obj) 具体可以看源码 Object iActivityManager = field1.get(amn); IamInvocationHandler iamInvocationHandler = new IamInvocationHandler(iActivityManager); Object object = Proxy.newProxyInstance(iamInvocationHandler.getClass().getClassLoader(),iActivityManager.getClass().getInterfaces(),iamInvocationHandler); field1.set(amn,object); } catch (Exception e) {
e.printStackTrace();
}
} class IamInvocationHandler implements InvocationHandler { Object iamObject; public IamInvocationHandler(Object iamObject) {
this.iamObject = iamObject;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Log.e(TAG, method.getName());
if ("startActivity".equals(method.getName())) {
Log.e(TAG, "要开始启动了 啦啦啦啦啦啦 ");
}
return method.invoke(iamObject, args);
}
}
}
 
image.png

测试也没问题

so that's all thanks for your time

参考文章:
Weishu's Notes
thanks weishu在某乎上答疑

作者:Allen_tong
链接:https://www.jianshu.com/p/5c6ff86331c8
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

[Hook] 免root,自己进程内,startActivity hook的几种姿势的更多相关文章

  1. [Hook] 免root,自己进程内,binder hook (ClipboardManager)

    cp from : http://weishu.me/2016/02/16/understand-plugin-framework-binder-hook/ Android系统通过Binder机制给应 ...

  2. android免root hook框架legend

    一.前言 Android中hook框架已经非常多了,最优秀的当属Xposed和Substrate了,这两个框架我在之前的文章都详细介绍过了,不了解的同学,可以转战这里:http://www.wjdia ...

  3. Linux进程内消息总线设计

    文章目录 Windows平台进程内消息总线 如果没有消息总线,会产生什么问题 死循环包含关系 高耦合.低内聚 消息总线 结构图 原理 生产者与总线的关系 总线与消费者的关系 Linux进程内消息总线设 ...

  4. HOOK函数(一)——进程内HOOK

    什么是HOOK呢?其实很简单,HOOK就是对Windows消息进行拦截检查处理的一个函数.在Windows的消息机制中,当用户产生消息时,应用程序通过调用GetMessage函数取出消息,然后把消息放 ...

  5. 钩子编程(HOOK) 安装进程内键盘钩子 (1)

    摘要:钩子能够监视系统或进程中的各种事件消息.截获发往目标窗体的消息并进行处理.这样,我们就能够在系统中安装自己定义的钩子,监视系统中特定事件的发生.完毕特定的功能,比方截获键盘.鼠标的输入.屏幕取词 ...

  6. x64内核HOOK技术之拦截进程.拦截线程.拦截模块

    x64内核HOOK技术之拦截进程.拦截线程.拦截模块 一丶为什么讲解HOOK技术. 在32系统下, 例如我们要HOOK SSDT表,那么直接讲CR0的内存保护属性去掉. 直接讲表的地址修改即可. 但是 ...

  7. Android进程so注入Hook java方法

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53769331 Andorid的Hook方式比较多,现在来学习下,基于Android ...

  8. SSDT Hook实现简单的进程隐藏和保护【转载】

    原文链接:http://www.blogfshare.com/ssdthook-hide-protect.html 原文作者:AloneMonkey SSDT Hook实现简单的进程隐藏和保护 Alo ...

  9. 64位下Hook NtOpenProcess的实现进程保护 + 源码 (升级篇 )

    64位下Hook NtOpenProcess的实现进程保护 + 源码 (升级篇 ) [PS: 如果在64位系统下,出现调用测试demo,返回false的情况下,请修改Hook Dll的代码] glhH ...

随机推荐

  1. maven centos7 环境变量

    tar -xvf apache-maven-3.3.9-bin.tar.gz mv apache-maven-3.3.9 /usr/local/apache-maven 文件存放好之后,设置环境变量, ...

  2. 【LOJ】#2186. 「SDOI2015」道路修建

    题解 就是线段树维护一下转移矩阵 分成两种情况,一种是前面有两个联通块,一种是前面有一个联通块 从一个联通块转移到一个联通块 也就是新加一列的三个边选其中两条即可 从一个联通块转移到两个联通块 不连竖 ...

  3. laravel Tinker报错 BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder

    进行模型关联操作, php artisan tinker 执行 $user = App\Models\User::find(1) $user->followings()->attach([ ...

  4. Going Home

    题意:n个人,进n个房子,每走一格花费1美元,每个房子只能进一人,求所有人进房子的最小花费.   就是推箱子 箱子最短行走距离 这题无法用bfs做 ! 用最小花费最大流 通过EK,Dinic,ISAP ...

  5. 002.NFS相关配置项

    一 配置文件(/etc/exports) 1.1 配置文件格式 <输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)] 二 输出目录 ...

  6. Jmeter脚本录制方法(一)分别使用Badboy录制和Jmeter自带的代理服务器录制

    Jmeter录制方式分三种,分别是:使用Badboy录制.Jmeter自带的代理服务器录制和手工录制,今天先介绍前两种录制方法. Badboy录制 Badboy是用C++开发的动态应用测试工具, 其拥 ...

  7. iOS开发安全 架构

    网络通讯.本地文件和数据.源代码三方面 网络通讯 安全的传输用户密码 客户端在登录时,使用公钥将用户的密码加密后,将密文传输到服务器.服务器使用私钥将密码解密,然后加盐 (Salt:在密码学中,是指通 ...

  8. Android-Drawable(三)

    Android-Drawable(三) 前两两篇文章已经学习了6个Drawable,接下来我们继续学习剩下的一些Drawable. Android系统的Drawable(一) Android系统的Dr ...

  9. 【FFT&NTT 总结】

    $FFT$总结 (因为还不会啊,,都没做过什么题,所以一边学一边打咯.. 1.主要是用来加速卷积形式的求和吧? $F*G(n)=F[i] × G[n-i]$ 平时是$O(n^2)$的,FFT可以$O( ...

  10. Prufer codes与Generalized Cayley's Formula

    Prufer序列 在一棵n个节点带标号树中,我们认为度数为1的点为叶子.n个点的树的Prufer序列是经过下面流程得到的一个长度为n-2的序列. 1.若当前树中只剩下两个点,退出,否则执行2. 2.找 ...