吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9960|回复: 48
上一主题 下一主题
收起左侧

[Android 原创] [Xposed模块] Xposed Hook实用方法详谈(7月3日更新)

  [复制链接]
跳转到指定楼层
楼主
wystudio 发表于 2023-7-1 16:22 回帖奖励
本帖最后由 wystudio 于 2023-7-4 15:58 编辑

缘起

正如各位坛友所见,目前论坛中已经有许多Xposed模块编写教程,但都是基础教程或针对于某一应用。仍需根据所Hook的应用来实际调整。

本文将列举并谈及一些常用Hook方法(适用于大部分应用),以方便新人的学习。

PS:本文为基础进阶教程,需要有一定的Xposed Hook,Java基础。若您为新手建议先观看一下教程:

@正己
七、Sorry,会Hook真的可以为所欲为-Xposed快速上手(上)模块编写  

八、Sorry,会Hook真的可以为所欲为-xposed快速上手(下)快速hook

实用方法列表

1.获取Activity对象(Android世界的大门)

①介绍:

众所周知,Android中的四大组件为:

  • Activity
  • Service
  • Content Provider
  • Broadcast Provider

Activity处于第一个可见其十分重要
而在Java中有了Activity对象,便可对Activity进行各种操作,如:打开另一个Activity,关闭当前Activity,获取Activity中的布局等。
同时Activity继承于Context,所以有了Activity方法也可以使用Context的方法。并且一些需要传入Context作为参数的方法也可以传入Activity对象,比如 new各种View和new一个Toast等。
总之得到了Activity对象,就犹如打开了Android世界的大门,世界之大任你遨游!  

下面介绍两种不同的获取方法,请根据实际情况选择使用

法一:获取指定的Activity对象

该方法用于在知道类名和onCreate实现方法名的前提下,获取指定的Activity对象

②步骤:
(1)首先要确定获取哪一个Activity对象。

这一步可以使用MT管理器中的Activity记录捕捉或直接打开清单文件查找。这里不再过多赘述。记下这个Activity类的包名路径,这里以 com.example.app.MainActivity 为例
Activity包名路径

(2)找到这个Activity的 onCreate 方法的实现方法名。

如果被Hook的应用没有被混淆,那名称就为 onCreate 。如果被混淆了,那就把应用拖入jsdx或用MT管理器等工具,去这个Activity的smail代码中找到名称。并记下这个名称。
这里假设没有被混淆,方法名为 onCreate
onCreate实现方法名

(3)Hook这个方法即可

现在我们得到了指定Activity的类名和 onCreate 实现方法名。
最后我们只需要Hook此类中的 onCreate 实现方法即可拿到Activity对象,具体代码如下:

XposedHelpers.findAndHookMethod("com.example.app.mainActivity", lpparam.classLoader, "onCreate",new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
       Activity mainActivity = (Activity) param.thisObject; //mainActivity就是最终得到的Activity对象
    }
});

最后的 mainActivity 就是我们获取到的Activity对象
现在你可以像编写一个APP一样,去使用它做各种事情

法二:获取当前Activity对象

此方法用于获取当前正在显示的Activity对象!

!注意:因为此方法属于非公开的API使用,可能因为Android版本的更迭而发生变化!  

该方法在一些系统被魔改的有些过分的系统中,可能出现未知错误。但是大部分系统都没问题

②步骤:
(1)Hook android.app.ActivityThread 中的 currentActivityThread 方法

通过Hook此方法获取到当前ActivityThread对象

(2)通过反射获取到当前Activity对象

先通过反射获取到Activity集合,再获取当前Activity
代码如下:

XposedHelpers.findAndHookMethod("android.app.ActivityThread", lpparam.classLoader, "currentActivityThread", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Object currentActivityThread = param.getResult(); // 获取当前ActivityThread对象

        Field activityField = currentActivityThread.getClass().getDeclaredField("mActivities");
        activityField.setAccessible(true);

        // 获取Activity集合
        Map<?, ?> activities = (Map<?, ?>) activityField.get(currentActivityThread);
        for (Object activityRecord : activities.values()) {
            Class<?> activityRecordClass = activityRecord.getClass();
            Field activityField2 = activityRecordClass.getDeclaredField("activity");
            activityField2.setAccessible(true);

            // 获取Activity对象
            Activity currentActivity = (Activity)activityField2.get(activityRecord);
            // currentActivity就为当前正在运行的Acticity对象
        }
    }
});

最后的currentActivity就为当前正在运行的Acticity对象

使用此方法,每当打开另一个Activity当前Activity被改变,都会自动重新获取当前Activity  

这里只介绍了两种获取Activity对象的方法,但其实不止这两种。请根据实际情况选择使用!

2.获取Activity的根布局

①介绍:

前一个方法介绍了如何获取Activity对象,现在有了对象,就可以进行各种操作,这里说说获取布局

②步骤:
(1)拿到Activity对象

通过方法1拿到Activity对象

(2)通过findViewById拿到布局

有些新手可能会问为什么 Activity 中有 setContentView(int id) 这个方法却没有 getContentView 这个方法呢?
这个我真没办法回答,但是我能告诉你怎么去通过Activity对象拿到根布局

其实通过Activity对象来获取根布局的方法有很多种,比如:

  • getRootView()
    但是通常拿不到id,这是为什么呢?这是因为拿到的getRootView 是在ContentView外的DecorView。

    在《Android群英传》中说到:Android的Window对象由PhoneWindow实现, DecorView将显示内容呈现在PhoneWindow上,所有的View监听由 WindowManagerService接收,通过Activity对象回调onClickListener; DecorView包含TitleView 和ContentView。
    这就是为什么 requestWindowFeature(Window.FEATURE_NO_TITLE)要放在setContent之前。
    ---来自Android群英传

  • getWindow().getDecorView().getRootView()
    此方法高版本安卓获取不到

这些方法要么高版本获取不到,要么耗时巨大。下面作者介绍的这个方法则将完美避开这些问题。
具体代码如下:

//通过方法1拿到mainActivity
ViewGroup rootLayout = (ViewGroup)mainActivity.findViewById(android.R.id.content);

这个rootLayout就是根布局,为ViewGroup类型。
有了根布局就可以对Activity中的组件进行操作,例如:

//获取根布局下的第一个View
View view = rootLayout.getChildAt(0);

//删除根布局下的第一个View
rootLayout.removeViewAt(0);

//向根布局中添加View
TextView textView = new TextView(mainActivity);
textView.setText("我是新添加的TextView");
rootLayout.addView(textView);

//......
③情景模拟:

当我们在Hook某个应用时,发现mainActivity中有一个广告一直阻挡我们的视线,十分苦恼。但又不想去分析广告代码,怎么才能去除呢?
其实我们可以通过Hook得到这个广告所在页面的根布局,然后通过布局助手等软件,找到这个广告组件的id,然后进行去除。如果没有id,那么就只有分析布局结构,然后拿到这个广告组件的对象进行去除。
这里我们假设拿不到这个id。
通过布局结构分析发现这个广告组件布局结构如下:  

  • 根布局
    • RelativeLayout
      • ADView (广告组件)
      • xxx
    • LinearLayout
    • xxx
(1)先通过根布局拿到RelativeLayout

根据布局结构分析,RelativeLayout为根布局下第一个组件
使用 getChildAt(int id) 方法获取RelativeLayout:

//通过方法2拿到根布局 rootLayout
RelativeLayout relativeLayout = (RelativeLayout) rootLayout.getChildAt(0);
(2)再通过RelativeLayout删除广告组件

根据布局结构分析,广告组件为 RelativeLayout 下的第一个组件
使用 removeViewAt(int id) 方法删除广告组件:

relativeLayout.removeViewAt(0);

至此,这个广告组件已经被我们成功去除!

3.获取ApplicationContext

①介绍:

实用方法1讲述了如何获取Activity对象,但是有些时候我们并不需要Activity对象也懒得去分析Activity所在的类名。我们只需要一个ApplicationContext(应用上下文)
并且我们只需要在应用启动时Hook得到它,并把它存在变量中,后续就可以直接拿来使用
并且ApplicationContext不会因为当前Activity的改变而无法使用。它随应用启动而创建,随应用终止而销毁。在应用的任何一个地方都可以使用它。  

下面介绍两种不同的获取方法,请根据实际情况选择使用

法一:通过Application获取Context

该方法通过Hook应用启动时的Application类中的attach方法来拿到Context

②步骤:
(1)Hook Application 类中的 attach 方法

attach方法中的参数就是我们需要的Context
代码如下:

XposedHelpers.findAndHookMethod(Application.class,"attach",new XC_MethodHook(){
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Context context= (Context) param.args[0];
        //这个context就为我们需要的ApplicationContext
    }
});

最后context就为我们需要的ApplicationContext  

此方法可用于一些动态加载dex的APP,如各种商业级APP

③原理分析:

为什么Application类中的attach方法参数是一个Context呢?这个参数又是谁传给它的呢?  

由于Android是开源的,我们查阅Android的源代码得到:  

(1)先由 ActivityThread. 中的 handleBindApplication 方法创建Application
private void handleBindApplication(AppBindData data) {
    ...
    // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
    // implementation to use the pool executor.  Normally, we use the
    // serialized executor as the default. This has to happen in the
    // main thread so the main looper is set right.
    if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
        AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    ...
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    ...
        app = data.info.makeApplication(data.restrictedBackupMode, null);

        ...

        mInitialApplication = app;

        ...
        try {
            mInstrumentation.callApplicationOnCreate(app);
        ...
}

可以看到在handleBindApplication又调用了makeApplication方法构造Application

(2)再通过 LoadedApk 中的 makeApplication 构造Application
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    ...
    return app;
}

可以看到makeApplication通过ContextImpl.createAppContext构造了contextImpl,然后用newApplication构造了Application

(3)然后调用InstrumentationnewApplication 方法
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}

可以看出newApplication先构造了Application,最后再调用attach方法并传入由makeApplication中创建的context作为参数

(4)最后调用 Application 中的 attach 方法
/* package */ final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

可以看到先调用了attachBaseContext,然后给mLoadedApk赋值

总结:

Application中的attach方法是由Instrumentation中的newApplication方法在Application创建时调用的
传入的参数Context是在LoadedApk中的makeApplication方法创建的

法二:通过ActivityThread获取Context

该方法通过HookActivityThread中的getSystemContext方法来获取Context

②步骤:
(1)获取当前ActivityThread对象

通过HookActivityThread中的currentActivityThread方法来获取当前ActivityThread对象

(2)调用getSystemContext方法

调用当前ActivityThread对象中的getSystemContext方法
代码如下:

XposedHelpers.findAndHookMethod("android.app.ActivityThread", lpparam.classLoader, "currentActivityThread",new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Object currentActivityThread = param.getResult(); // 获取当前ActivityThread对象
        Context context= (Context)XposedHelpers.callMethod(currentActivityThread,"getSystemContext");
        //context即为我们要的ApplicationContext
    }    
});

context即为我们要获取的ApplicationContext

4.未完待续。。。

写在最后

本文为作者学习Xposed Hook时,发现的一些实用,常用方法记录以及一些思路记录,供大家学习!
本文如有错误,请大佬指正!  

本文已同步发至作者博客:立即查看(无联系方式)

参考文档

《Android群英传》 徐宜生 电子工业出版社
Android Studio Xposed模块编写(二) -Gordon0918 - 博客园

免费评分

参与人数 19威望 +1 吾爱币 +41 热心值 +17 收起 理由
juziss + 1 我很赞同!
cqked + 1 用心讨论,共获提升!
Darkerヘ疯狂 + 1 + 1 有点东西哦!继续加油!
ql_zth + 2 + 1 热心回复!
友人A♂ + 1 + 1 我很赞同!
正义天下 + 1 + 1 谢谢@Thanks!
xiaolong666 + 1 + 1 用心讨论,共获提升!
Tonyha7 + 1 + 1 谢谢@Thanks!
luanyaolingwu + 1 我很赞同!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
芽衣 + 2 + 1 用心讨论,共获提升!
高高的洁白的花 + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
LeeCocoR + 1 + 1 我很赞同!
小兴818 + 1 + 1 谢谢@Thanks!
万能的楼下小黑 + 1 + 1 谢谢@Thanks!
liy00000 + 1 + 1 我很赞同!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lnxctz + 2 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| wystudio 发表于 2023-7-1 21:46 |楼主
本帖最后由 wystudio 于 2023-7-2 13:08 编辑
小k666 发表于 2023-7-1 21:23
每个版本更新,混淆后类名都不一样,有没有好的自适应方法

其实还可以通过Hook系统中的API来拿到Activity对象,但是不好判断获取的对象为哪一个Activity的。
等我看看,把这个方法也加上
推荐
tyc600 发表于 2024-11-13 21:43
如何将hook到的activity对象保存为全局变量,供其他类调用?楼主能不能详细说一下。谢谢&#128522;
3#
klxn0-0 发表于 2023-7-1 17:38
4#
 楼主| wystudio 发表于 2023-7-1 19:39 |楼主
klxn0-0 发表于 2023-7-1 17:38
onCreat 应为onCreate吧

对,我的问题
5#
OliverHarrison 发表于 2023-7-1 19:39
谢谢分享!!!!
6#
fnckyon2014 发表于 2023-7-1 20:33
6,刚好最近在学习
7#
正己 发表于 2023-7-1 21:21
内容可以再完善些哦,先提前一威望,期待后续教程内容
8#
小k666 发表于 2023-7-1 21:23
每个版本更新,混淆后类名都不一样,有没有好的自适应方法
9#
 楼主| wystudio 发表于 2023-7-1 21:39 |楼主
正己 发表于 2023-7-1 21:21
内容可以再完善些哦,先提前一威望,期待后续教程内容

好的,谢谢版主
头像被屏蔽
10#
wertop 发表于 2023-7-1 21:54
提示: 该帖被管理员或版主屏蔽
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 09:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表