Android Hook—Xposed

Xposed介绍

Xposed框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。

官网地址: http://repo.xposed.info/
源码地址: https://github.com/rovo89

Xposed安装

Android 6.0.x
https://pan.baidu.com/s/1qY7oW36 密码:r3mh

  • 设备需要root
  • recovery 刷入 xposed-v78-sdk23-arm.zip
  • 安装xposed.apk

Xposed使用

打开Xposed Installer

  • 框架——安装、检查框架运行状态
  • 模块——列出当前可用模块以及状态
  • 下载——框架商店
  • 日志——Xposed中日志记录

注意:每次启用或者关闭模块均需要重启设备

Xposed模块开发

Xposed模块的安装包实际上就是一个有着特殊的AndroidManifest.xml和添加了XposedBridgeApi.jar包的apk文件,所以同样可以用Android Studio 来开发。同时我们在开发时需获取的目标(被劫持的)apk的信息有:

  • 包名
  • 类名
  • 函数名
  • 函数参数

LoginDemo

package com.example.xuanxuan.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private Button bt1;
    private EditText name1;
    private EditText password1;
    private final String ACCOUNT="xuanxuan";
    private final String PASSWORD="123456";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt1 = (Button) findViewById(R.id.id_button);
        name1 = (EditText)findViewById(R.id.id_name);
        password1 = (EditText)findViewById(R.id.id_password);

        bt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = name1.getText().toString();
                String password = password1.getText().toString();
                if(isOK(username,password)){
                    Toast.makeText(MainActivity.this,"login success",Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(MainActivity.this,"login failed",Toast.LENGTH_SHORT).show();
                }

            }
        });

    }
    private boolean isOK (String argc,String argv){
        return argc.equals(ACCOUNT)&&argv.equals(PASSWORD);
    }
}

HookDemo

  1. 新建一个不需要Activity的空白工程。
  2. 在空工程的java文件夹中新建一个类,该类就是一个模块类,这里命名为“Module”。
  3. 配置AndroidManifest.xml,其中的meta-data内容要照搬。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.xuanxuan.hackdemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <meta-data
            android:name="xposedmodule"
            android:value="true"/>

        <meta-data
            android:name="xposeddescription"
            android:value="Hook Test!"/>

        <meta-data
            android:name="xposedminversion"
            android:value="54"/>
    </application>
</manifest>
  1. 在main文件夹下创建一个assets文件夹,并在里面创建一个名为“xposed_init”的普通文件,在里面添加一句字符串,即包名+模块类名,如com.example.xuanxuan.hackdemo.Module
  2. 新建一个名为lib的文件夹,注意不是libs。然后放入XposedBridgeApi.jar包,并在该jar包上右键Add as Library。

XposedBridgeApi.jar下载地址:
https://forum.xda-developers.com/xposed/xposed-api-changelog-developer-news-t2714067

  1. 编写模块类Module.java
package com.example.xuanxuan.hackdemo;

import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * Created by xuanxuan on 2018/1/15.
 */

public class Module implements IXposedHookLoadPackage{


    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if(loadPackageParam.packageName.equals("com.example.xuanxuan.myapplication")){   //包名
            XposedHelpers.findAndHookMethod(
                    "com.example.xuanxuan.myapplication.MainActivity",  //类名
                    loadPackageParam.classLoader,
                    "isOK", //函数名
                    String.class,
                    String.class,   //函数参数
                    new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            XposedBridge.log("账号:"+(String)param.args[0]+"   密码:"+(String)param.args[1]);
                            Log.d("zz","账号:"+(String)param.args[0]+"   密码:"+(String)param.args[1]);
                        }

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            Log.d("zz", param.getResult().toString());
                        }
                    });
        }
    }
}

说明:

  • Module继承了IXposedHookLoadPackage接口,当系统加载应用包的时候回调 handleLoadPackage;

  • XposedHelpers的静态方法findAndHookMethod就是hook函数的的方法,其参数对应为:

    1. 类名,必须要有包名前缀,即“packageName+className”
    2. loadPackageParam.classLoader(照写)
    3. 方法名
    4. 参数类型(根据所hook方法的参数的类型,即有多少个写多少个,加上.class)
    5. XC_MethodHook回调接口

如果代码被混淆过,即使你明知道代码中要hook的类名和方法名,但都不一定能用,必须以smali中的名字为准,比如:isOk()混淆之后在smali中的函数名为a,那么hook的时候就必须写a,而不是isOK,第一个参数类名同理!

  • 参数里有一个监听类XC_MethodHook,该类在hook前后回调,通过回调方法的MethodHookParam可以拦截到函数参数。
  1. 打包 Build->Generate Signed APK生成一个APK,并安装在手机,重启。即可在Xposed日志中查看到被记录的登录信息。

参考

Android 6.0.x 系统安装xposed框架
http://blog.csdn.net/u011303443/article/details/51031170

XPosed入门与登陆劫持演示
https://www.csdn.net/article/2015-08-14/2825462

Android逆向分析之Xposed的hook技术
http://blog.csdn.net/qq_18870023/article/details/51753587

Xposed模块编写的那些事
http://www.freebuf.com/articles/terminal/114910.html