備忘録

備忘録

Androidで他アプリのメソッドをhookする方法

注意

この記事の内容は2016年10月27日時点の物です。
内容が古くなっている場合があります。

Ⅰ はじめに

Xposed Frameworkを利用します。 ネイティブ(C/C++)で書かれたコードはhookできません。

また、初歩的な事は以下の記事を参照して下さい。 https://kagasu.hatenablog.com/entry/2020/04/23/034804

Ⅱゴール

今回は以下のmyAdd をhookして、足し算の結果を書き換える事をゴールとします。

private int myAdd(int val1, int val2) {
    return val1 + val2;
}

f:id:kagasu:20200423035135p:plain

Ⅲ hookする

1. 戻り値を999に書き換える

final Class<?> _class = XposedHelpers.findClass("net.kagasu.calculator.MainActivity", lpparam.classLoader);

XposedBridge.hookAllMethods(_class, "myAdd", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        //super.afterHookedMethod(param);
        // 戻り値を999に書き換える
        param.setResult(999);
    }
});

2. 引数をとり、戻り値を書き換える

final Class<?> _class = XposedHelpers.findClass("net.kagasu.calculator.MainActivity", lpparam.classLoader);

XposedBridge.hookAllMethods(_class, "myAdd", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        //super.afterHookedMethod(param);
        int val1 = (int)param.args[0];
        int val2 = (int)param.args[1];

        // 掛け算にする
        param.setResult(val1 * val2);
    }
});

ソースコード

1. Androidアプリ

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Button button1;
    private EditText et1, et2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et1 = (EditText) findViewById(R.id.editText1);
        et2 = (EditText) findViewById(R.id.editText2);
        button1 = (Button) findViewById(R.id.button1);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int val1 = Integer.valueOf(et1.getText().toString());
                int val2 = Integer.valueOf(et2.getText().toString());

                int result = myAdd(val1, val2);

                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("result")
                        .setMessage(String.valueOf(result))
                        .setPositiveButton("OK", null)
                        .show();

            }
        });
    }

    private int myAdd(int val1, int val2) {
        return val1 + val2;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="net.kagasu.calculator.MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="numberDecimal"
                android:layout_weight="1"
                android:id="@+id/editText1" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="+" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:inputType="numberDecimal"
                android:layout_weight="1"
                android:id="@+id/editText2" />

        </LinearLayout>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Calculate"
            android:id="@+id/button1"/>

    </LinearLayout>

</RelativeLayout>

2. Xposedモジュール

XposedHookAdd.java

package net.kagasu.xposedhookadd;

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.LoadPackageParam;

public class XposedHookAdd implements IXposedHookLoadPackage {
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if(!lpparam.packageName.equals("net.kagasu.calculator")) {
            return;
        }

        final Class<?> _class = XposedHelpers.findClass("net.kagasu.calculator.MainActivity", lpparam.classLoader);

        XposedBridge.hookAllMethods(_class, "myAdd", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                //super.beforeHookedMethod(param);
                XposedBridge.log("[" + lpparam.packageName + "] beforeHookedMethod");
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                //super.afterHookedMethod(param);
                XposedBridge.log("[" + lpparam.packageName + "] afterHookedMethod");

                int val1 = (int)param.args[0];
                int val2 = (int)param.args[1];

                XposedBridge.log("引数1: " + val1);
                XposedBridge.log("引数2: " + val2);

                XposedBridge.log("書き換える前の値: " + param.getResult());

                // 掛け算にする
                param.setResult(val1 * val2);
                // 999にする
                //param.setResult(999);

                XposedBridge.log("書き換えた後の値: " + param.getResult());
            }
        });
    }
}

参考