Featured image of post 安卓逆向入门五:Xposed和hook入门

安卓逆向入门五:Xposed和hook入门

关键词:Xposed原理和常用apiAndroid Studio安装以及环境配置、hook、借助lspatch实现免root注入SimpleHook快速hook

0x00 什么是Xposed

Xposed 是一款无需修改 APK 即可影响程序运行的 Hook 框架,基于它可以开发功能强大的插件 APP,实现对目标应用的注入、拦截、修改等操作,且多个功能不冲突的模块可同时运行。

简单来说,Xposed 的核心是 “在不改变原应用代码的情况下,动态干预其运行逻辑”,这使得它成为安卓逆向、功能扩展的重要工具。

Xposed (维基百科)

0x01 Xposed原理

Xposed 的核心是劫持 Zygote 进程(安卓系统的进程孵化器,所有应用进程都由它创建),具体过程如下:

  1. 用自定义实现的app_process替换系统原生的app_process
  2. 加载额外的 XposedBridge.jar 包,将程序入口从系统默认的com.android.internal.os.ZygoteInit.main()替换为de.robv.android.xposed.XposedBridge.main()
  3. 改造后的 Zygote 进程创建的所有 Dalvik/ART 虚拟机都会被 Hook,从而实现对所有应用进程的动态干预。

通俗来讲,hook就是狸猫换太子

0x02 Xposed的发展及免root框架

随着安卓版本迭代,Xposed 衍生出多个分支,下表整理了主流框架的特点:

名称 地址 支持版本 是否免root
xposed https://github.com/rovo89/Xposed 2.3-8.1
EDXposed https://github.com/ElderDrivers/EdXposed 8.0-10
LSPosed https://github.com/LSPosed/LSPosed 8.1-13
VirtualXposed https://github.com/android-hacker/VirtualXposed 5.0-10.0
太极 https://www.coolapk.com/apk/me.weishu.exp 5.0-13
两仪 https://www.coolapk.com/apk/io.twoyi 8.1-13
天鉴 https://github.com/Katana-Official/SPatch-Update 6-10

0x03 Xposed可以用来做什么?

  • 功能增强:修改应用布局(如上帝模式)、去除广告(如知乎去广告模块);

  • 数据干预:劫持参数 / 返回值(如微信防撤回、步数修改、一键新机);

  • 自动化操作:微信自动抢红包、抖音自动操作等;

  • 逆向辅助:动态分析应用逻辑、绕过加密 / 验证(如XServer)。

2022 最好的Xposed模块: GravityBox, Pixelify, XPrivacyLua

基于Xposed的抖音爬虫,抖音风控后自动一键新机,模拟一个全新的运行环境

基于xposed的frida持久化方案

A Xposed Module for Android Penetration Test, with NanoHttpd.

GravityBox

Xposed-Modules-Repo]

一个旨在使QQ变得更好用的开源Xposed模块

杜比大喇叭

哔哩漫游

曲境

自动化创建Xposed模块及钩子,让Xposed模块编写时只需关注钩子实现

0x04 Xposed环境配置

这里下载的是正己提供的Ubuntu虚拟机 密码:toor

内置:

  • Frida开发环境
  • 动态分析及开发工具:android-studio
  • 动态分析工具:ddms
  • 静态分析工具:jadx1.4.4
  • 动静态分析工具:jeb
  • 动态分析工具:集成HyperPwn
  • 静态分析工具:010 editor
  • 抓包工具:Charles
  • 抓包工具:WireShark
  • 动态分析工具:unidbg

  • 1.Android Studio创建新项目

  • 2.将下载的xposedBridgeApi.jar包拖进libs文件夹

  • 3.右击jar包,选择add as library

  • 4.修改AndroidManifest.xml文件配置

    • 将如下代码复制到xml文件中,如图

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      <!-- 是否是xposed模块,xposed根据这个来判断是否是模块 -->
      <meta-data
          android:name="xposedmodule"
          android:value="true" />
      <!-- 模块描述,显示在xposed模块列表那里第二行 -->
      <meta-data
          android:name="xposeddescription"
          android:value="这是一个Xposed模块" />
      <!-- 最低xposed版本号(lib文件名可知) -->
      <meta-data
          android:name="xposedminversion"
          android:value="89" />
      

  • 5.修改build.gradle,将此处修改为compileOnly 默认的是implementation

    implementation 使用该方式依赖的库将会参与编译和打包

    compileOnly 只在编译时有效,不会参与打包

  • 6.新建–>Folder–>Assets Folder,创建xposed_init(不要后缀名):只有一行代码,就是说明入口类

  • 7.新建Hook类,实现IXposedHookLoadPackage接口,然后在handleLoadPackage函数内编写Hook逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    import de.robv.android.xposed.IXposedHookLoadPackage; 
    import de.robv.android.xposed.callbacks.XC_LoadPackage;
    
    public class Hook implements IXposedHookLoadPackage {
        @Override
        public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
    
        }
    }
    

    继承了IXposedHookLoadPackag便拥有了hook的能力

0x05 Xposed常用API

1.Hook普通方法

  • 修改返回值

    1
    2
    3
    4
    5
    6
    7
    
    XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() {
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
            param.setResult(999);
        }
    });
    
  • 修改参数

    1
    2
    3
    4
    5
    6
    7
    
    XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            super.beforeHookedMethod(param);
            String a = "pt";
            param.args[0] = a;    }
    });
    

2.Hook复杂&自定义参数

1
2
3
4
5
6
7
8
Class a = loadPackageParam.classLoader.loadClass("类名");
XposedBridge.hookAllMethods(a, "方法名", new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);

        }
});

3.Hook替换函数

使用XC_MethodReplacement直接替换原方法:

1
2
3
4
5
6
7
Class a = classLoader.loadClass("类名")
XposedBridge.hookAllMethods(a,"方法名",new XC_MethodReplacement() {  
    @Override  
    protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {  
        return "";  
    }  
});

上图中日志这里的 “这是替换函数” 就被替换无了

4.Hook加固通杀

加固应用会动态加载 dex,需在Application.attach时获取加载后的类加载器:

1
2
3
4
5
6
7
8
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {  
    @Override  
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
        Context context = (Context) param.args[0];  
        ClassLoader classLoader = context.getClassLoader();
        //hook逻辑在这里面写  
    }  
});

5.Hook变量

  • 静态变量与实例变量
    • 静态变量(static):类被初始化,同步进行初始化
    • 非静态变量:类被实例化(产生一个对象的时候),进行初始化

6.Hook构造函数

  • 无参构造函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    XposedHelpers.findAndHookConstructor("com.zj.wuaipojie.Demo", classLoader, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            super.beforeHookedMethod(param);
        }
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
        }
    });
    
  • 有参构造函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    XposedHelpers.findAndHookConstructor("com.zj.wuaipojie.Demo", classLoader, String.class, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            super.beforeHookedMethod(param);
        }
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
        }
    });
    

7.Hook multiDex方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {  
    @Override  
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
        ClassLoader cl= ((Context)param.args[0]).getClassLoader();  
        Class<?> hookclass=null;  
        try {  
            hookclass=cl.loadClass("类名");  
        }catch (Exception e){  
            Log.e("zj2595","未找到类",e);  
            return;        
        }  
        XposedHelpers.findAndHookMethod(hookclass, "方法名", new XC_MethodHook() {  
            @Override  
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
            }        
        });  
    }  
});

8.主动调用

  • 静态方法

    1
    2
    
    Class clazz = XposedHelpers.findClass("类名",lpparam.classLoader);
    XposedHelpers.callStaticMethod(clazz,"方法名",参数(非必须));
    
  • 实例方法

    1
    2
    
    Class clazz = XposedHelpers.findClass("类名",lpparam.classLoader);
    XposedHelpers.callMethod(clazz.newInstance(),"方法名",参数(非必须));
    

9.Hook内部类

内部类名格式为外部类$内部类(如com.zj.wuaipojie.Demo$InnerClass

1
2
3
4
5
6
7
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo$InnerClass", lpparam.classLoader, "innerFunc",String.class,  new XC_MethodHook() {  
    @Override  
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
        super.beforeHookedMethod(param);  

    }  
});

10.反射大法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Class clazz = XposedHelpers.findClass("com.zj.wuaipojie.Demo", lpparam.classLoader);
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo$InnerClass", lpparam.classLoader, "innerFunc",String.class,  new XC_MethodHook() {  
    @Override  
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
        super.beforeHookedMethod(param);  
        //第一步找到类
        //找到方法,如果是私有方法就要setAccessible设置访问权限
        //invoke主动调用或者set修改值(变量)
        Class democlass = Class.forName("com.zj.wuaipojie.Demo",false,lpparam.classLoader);  
        Method demomethod = democlass.getDeclaredMethod("refl");  
        demomethod.setAccessible(true);  
        demomethod.invoke(clazz.newInstance());  
    }  
});

11.遍历所有类下的所有方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
XposedHelpers.findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {  
    @Override  
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
        super.afterHookedMethod(param);  
        Class clazz = (Class) param.getResult();  
        String clazzName = clazz.getName();  
        //排除非包名的类  
        if(clazzName.contains("com.zj.wuaipojie")){  
            Method[] mds = clazz.getDeclaredMethods();  
            for(int i =0;i<mds.length;i++){  
                final Method md = mds[i];  
                int mod = mds[i].getModifiers();  
                //去除抽象、native、接口方法  
                if(!Modifier.isAbstract(mod)  
                    && !Modifier.isNative(mod)  
                    &&!Modifier.isAbstract(mod)){  
                    XposedBridge.hookMethod(mds[i], new XC_MethodHook() {  
                        @Override  
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
                            super.beforeHookedMethod(param);  
                            Log.d("zj2595",md.toString());  
                        }  
                    });  
                }  

           }  
        }  

    }  
});

0x06 Xposed妙用

  • 字符串赋值定位:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    XposedHelpers.findAndHookMethod("android.widget.TextView", lpparam.classLoader, "setText", CharSequence.class, new XC_MethodHook() {  
        @Override  
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
            super.beforeHookedMethod(param);  
            Log.d("zj2595",param.args[0].toString());  
                    if(param.args[0].equals("已过期")){  
                        printStackTrace();  
                    }
        }  
    });
    private static void printStackTrace() {  
        Throwable ex = new Throwable();  
        StackTraceElement[] stackElements = ex.getStackTrace();  
        for (int i = 0; i < stackElements.length; i++) {  
            StackTraceElement element = stackElements[i];  
            Log.d("zj2595","at " + element.getClassName() + "." + element.getMethodName() + "(" + element.getFileName() + ":" + element.getLineNumber() + ")");  
        }  
    }
    

  • 点击事件监听

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    Class clazz = XposedHelpers.findClass("android.view.View", lpparam.classLoader);
    XposedBridge.hookAllMethods(clazz, "performClick", new XC_MethodHook() {  
        @Override  
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
            super.afterHookedMethod(param);  
            Object listenerInfoObject = XposedHelpers.getObjectField(param.thisObject, "mListenerInfo");  
            Object mOnClickListenerObject = XposedHelpers.getObjectField(listenerInfoObject, "mOnClickListener");  
            String callbackType = mOnClickListenerObject.getClass().getName();  
            Log.d("zj2595",callbackType);  
        }  
    });
    

  • 改写布局

    在 Activity 初始化后修改 UI 元素(如隐藏图片):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    XposedHelpers.findAndHookMethod("com.zj.wuaipojie.ui.ChallengeSixth", lpparam.classLoader,  
            "onCreate", Bundle.class, new XC_MethodHook() {  
        @Override  
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
            super.afterHookedMethod(param);  
            View img = (View)XposedHelpers.callMethod(param.thisObject,  
                    "findViewById", 0x7f0800de);  
            img.setVisibility(View.GONE);  
    
        }  
    });
    
修改前 修改后

0x07 Lsposed 免root实现

  • Lsposed:LSPosed 框架的无 Root 实现方案,通过将 dex 文件与 so 文件插入到目标 APK(安卓应用安装包)中,实现 Xposed API(应用程序编程接口)的集成。
  • 核心原理:重打包目标 APK,将模块代码集成到其中,避免对系统环境的依赖。

0x07 Xposed快速Hook—–SimpleHook

SimpleHook 是 GitHub 用户 littleWhiteDuck 开发的一款 Android 轻量型 Hook 工具,核心定位是 “简单易用”—— 无需复杂配置即可实现常见 Hook 需求,适合对 Hook 操作复杂度要求低的用户;若需复杂功能(如自定义脚本)或更多扩展能力,官方推荐搭配 jshook(复杂功能)曲境(电脑端浏览器操作)或算法助手(扩展功能)使用。

  • 使用步骤
    • 在 LSPosed 中启用 SimpleHook 模块;
    • 选择目标应用,添加 Hook 配置(类名、方法名、参数类型等);
    • 重启应用,配置生效。

工具覆盖多种常见 Hook 场景,部分关键模式及示例如下:

模式类型 功能说明 关键特点
Hook 返回值 修改目标方法的返回结果,支持基本类型(如 boolean、int)、字符串及 null 参数类型留空表示无参,多参数用英文逗号分隔(如 java.lang.String,int
Hook 返回值 + 进阶功能,支持通过 JSON 格式将数据转为目标对象(依赖 Gson) 需填写 “返回值类名”,修改值为 JSON 字符串(如 {"isHook":false,"level":10000}
Hook 参数值 修改目标方法的输入参数,支持部分参数或全部参数修改 未修改的参数留空(如仅改第 2、3 个参数,修改值填 ,啦啦啦,99
中断执行 拦截目标方法执行,使其不触发原有逻辑 无需填写修改值,仅需配置类名、方法名及参数类型
特殊方法 Hook - 构造方法:方法名填写 <init>- 所有同名方法:参数类型填 *- 类内所有方法:方法名填 * 适配不同场景下的批量或特殊方法 Hook 需求
变量 Hook - 静态变量:支持 before/after 两个 Hook 点(方法执行前后修改)- 实例变量:仅支持在本类方法执行后修改,不可跨类 仅支持基本类型和字符串,需填写 “变量所在类名”“变量名”
记录类模式(3 种) 记录参数值、记录返回值、记录参返(参数 + 返回值) 数组或 List 类型参数 / 返回值会自动转为 JSON 格式,便于查看

该软件还满足自定义hook,具体细节请前往SimpleHook学习。

参考文档

《安卓逆向这档事》七、Sorry,会Hook真的可以为所欲为-Xposed快速上手(上)

《安卓逆向这档事》八、Sorry,会Hook真的可以为所欲为-xposed快速上手(下)

源码编译(2)——Xopsed源码编译详解 Xposed Hook技巧,代{过}{滤}理abstract Xposed callMethod 如何传入接口参数

XPOSED魔改一:获取特征 Lsposed 技术原理探讨 && 基本安装使用

前途似海,来日方长。

<