Android Hook框架Xposed入门

转:http://chendd.com/blog/2016/05/15/learn_xposed/


一.基础知识

       Xposed是Android平台上较为出名的一个开源框架。在这个框架下,我们可以加载很多插件App,这些插件App可以直接或间接操纵普通应用甚至系统上的东西。Xposed原理上是Hook Android 系统的核心进程Zygote来达到修改程序运行过程和结果。讲到这里,可能有人会问什么是Hook?什么是Zygote?

  • Hook(钩子),钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

  • Zygote(Android进程名),Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要取名英文本意为受精卵的Zygote原因吧。

       由于Xposed框架Hook了Android的核心进程Zygote,而其他应用启动都是从Zygote进程fork而来,就够达到针对系统上所有的应用程序进程的Hook。

二.Xposed简介

       rovo89大神github主页,如图所示

       image

主页大致可以看出,xposed主要由三个项目来组成的

  • Xposed,Xposed的C++ 部分,主要是用来替换/system/bin/app_process,并为XposedBridge提供JNI方法
  • XposedBridge,Xposed 提供的jar文件,app_process启动过程中会加载该jar包,其他的Modules的开发都是基于该jar包
  • XposedInstaller,Xposed的安装包,提供对基于Xposed框架的Modules的管理

xposed目前已逐步支持ART虚拟机,兼容android 5.0以上版本

三.Xposed使用

       在Android 4.0以上Android设备(需root权限,建议直接用模拟器)安装XposedInstaller

启动XposedInstaller点击 【框架】

image

点击 【安装/更新】 并重启,再点击框架看到看到 激活底下两个都是绿色 代表框架安装成功

image

我们可以点击【下载】来查看热门插件进行安装

安装完插件点击【模块】进行勾选激活

之后还需重启,插件才能生效。大家可以自己下载几个插件玩玩,本文重点不在这,就不演示了。

四.编写插件

这里我们hook自己编写的一个小的登录app来获取用户名密码。

界面比较简单,输入用户名密码点击登录弹出用户输入的密码

image

界面代码

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
31
32
33
34
35
36
37
38
39
40
public EditText et_username;
  // 属性为private 时普通反射获取不到该对象
  // private  EditText et_password;
  public EditText et_password;
  
  public Button bt_login;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_username = (EditText) findViewById(R.id.et_username);
      et_password = (EditText) findViewById(R.id.et_password);
      
      bt_login = (Button) findViewById(R.id.bt_login);
      bt_login.setOnClickListener(this);
    }


    private boolean isCorrectInfo(String username, String password) {
      // 校验用户名密码是否正确,直接返回true
      return true;
    }


  @Override
  public void onClick(View v) {
      switch (v.getId()) {
      case R.id.bt_login:
          if(isCorrectInfo(et_username.getText().toString(), et_password.getText().toString())) {
              // 帐号密码校验成功,弹出当前密码
              Toast.makeText(MainActivity.this, "password:"+et_password.getText().toString(), Toast.LENGTH_SHORT).show();
          }
          break;

      default:
          break;
      }
  }
1.在AndroidManifest.xml文件中配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

  <!-- 标记xposed插件 -->
    <meta-data
        android:name="xposedmodule"
        android:value="true" />
  <!-- 模块描述 -->
    <meta-data
        android:name="xposeddescription"
        android:value="登录hook例子!" />
  <!-- 最低版本号 -->
    <meta-data
        android:name="xposedminversion"
        android:value="54" />

</application>
2.导入其jar包

       XposedBridgeApi-.jar,下载完毕后我们需要将Xposed Library复制到lib目录(注意是lib目录,不是Android提供的libs目录),然后将这个jar包添加到Build PATH中。

3.声明主入口路径

需要在assets文件夹中新建一个xposed_init的文件,并在其中声明主入口类。如这里我们的主入口类为

1
com.example.xposedtest.HookUtil

image

4.使用findAndHookMethod方法Hook

       这是最重要的一步,我们之前所分析的都需要到这一步进行操作。如我们之前所分析的登陆程序,我们需要劫持,就是需要Hook其com.example.logintest.MainActivity中的isCorrectInfo方法。我们使用Xposed提供的findAndHookMethod直接进行MethodHook操作。在其Hook回调中使用XposedBridge.log方法,将登陆的账号密码信息打印至Xposed的日志中。具体操作如下所示

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
31
32
33
34
public class HookUtil implements IXposedHookLoadPackage{

  @Override
  public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
      // 标记目标app包名
        if (!lpparam.packageName.equals("com.example.logintest"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);

        // Hook MainActivity中的isCorrectInfo(String,String)方法
        // findAndHookMethod(hook方法的类名,classLoader,hook方法名,hook方法参数...,XC_MethodHook)
        XposedHelpers.findAndHookMethod("com.example.logintest.MainActivity", lpparam.classLoader, "isCorrectInfo", String.class,
                String.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("开始hook");
                        XposedBridge.log("参数1 = " + param.args[0]);
                        XposedBridge.log("参数2 = " + param.args[1]);
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("结束hook");
                        XposedBridge.log("参数1 = " + param.args[0]);
                        XposedBridge.log("参数2 = " + param.args[1]);

                    }
                });


  }

}
5.运行程序,查看效果

image

       重启Android设备,进入XposedInstaller点击【日志】查看,因为我们之前使用的是XposedBridge.log方法打印log,所以log都会显示在此处。我们发现我们需要劫持的账号密码都显示再来此处。

image

       这里由于demo是我们自己写的,所以知道hook它的帐号校验方法isCorrectInfo来获取用户名密码,如果有些程序账户校验没有封装方法呢?其实我们可以hook其它一些必有的方法,如button的onClick方法,甚至可以动态改变EditText的内容,做法如下:

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
31
32
33
34
35
36
37
38
39
40
41
public class HookUtil implements IXposedHookLoadPackage{

  @Override
  public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
      // 标记目标app包名
        if (!lpparam.packageName.equals("com.example.logintest"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);


        // Hook MainActivity中的onClick(View v)方法
        XposedHelpers.findAndHookMethod("com.example.logintest.MainActivity", lpparam.classLoader, "onClick", View.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

                        Class clazz = param.thisObject.getClass();
                        XposedBridge.log("class name:"+clazz.getName());

                        Field field = clazz.getField("et_password");// 密码输入框 id

                        EditText password = (EditText) field.get(param.thisObject);

                        String string = password.getText().toString();
                        XposedBridge.log("密码 = " + string);
                        // 设置新密码
                        password.setText("改你妹啊!!");

                    }
                });




  }

}

image

       点击登录按钮,发现输入框内容改变了

image

       之前登录app密码EditText声明为 private 时,导致反射获取报NoSuchFileException,原因是普通的反射不能获取私有变量, 改为EditText声明改为public后成功,或者不能改EditText声明时,可以用以下方法,无论公有私有都可以获取

1
2
3
4
5
6
7
// 输入框不为私有private 可通过以下方式获取 
// Field field = clazz.getField("et_password");// 密码输入框 id

// 通过类的字节码得到该类中声明的所有属性,无论私有或公有
Field field = clazz.getDeclaredField("et_password");
// 设置访问权限(这点对于有过android开发经验的可以说很熟悉)
field.setAccessible(true);

4.总结

       既然能成功Hook自己的App,那么系统应用和其它应用的也同理,只不过需要知道系统公开接口或者反编译获得相应的接口,下次讲解Hook不是自己的应用做些好玩的东西。

附:

本文源码: https://github.com/chendd/XposedTest.git

官方教程: https://github.com/rovo89/XposedBridge/wiki/Development-tutorial

官方例子: https://github.com/rovo89/XposedExamples

参考文章: http://www.csdn.net/article/1970-01-01/2825462


  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值