LSPosed-Native层Hook

前言

使用Xposed进行Android App Native层Hook的方式需要借助第三方Hook库,且使用起来十分麻烦,LSPosed经过改良后,支持了Native层的Hook,使用流程十分简单.下面通过一个示例来展示下LSPosed Native层的Hook流程.

测试Apk

MainActivity.java:

 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
package com.example.luodst;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.example.luodst.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'luodst' library on application startup.
    static {
        System.loadLibrary("luodst");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'luodst' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

native-lib.cpp:

 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
#include <jni.h>
#include <string>

#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "LuoHun", __VA_ARGS__);
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "LuoHun", __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "LuoHun", __VA_ARGS__);


extern "C" bool check(std::string strDst){
    return strstr(strDst.c_str(), "Luo");
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_luodst_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {

    if (check("Hun")){
        LOGE("check true")
    } else{
        LOGE("check false")
    }

    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

Hook示例

下面以Hook上面libluodst.so库中的check函数为例.

  1. 修改AndroidManifest.xml文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
android:multiArch="true"
android:extractNativeLibs="false"

<meta-data
    android:name="xposedmodule"
    android:value="true" />
<meta-data
    android:name="xposeddescription"
    android:value="这是一个xposed demo" />
<meta-data
    android:name="xposedminversion"
    android:value="82" />

AndroidManifest

  1. 将XposedBridgeApi.jar放到项目中的app/libs目录下

XposedAPI

  1. 配置build.gradle
1
2
3
compileOnly files('libs/XposedBridgeApi-82.jar')
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'

build

  1. 配置settings.gradle,在repositories中添加下面两个任意一个即可
1
2
maven { url 'https://maven.aliyun.com/repository/public/' }
//maven { url "https://api.xposed.info/" }

setting

  1. 新建一个类继承IXposedHookLoadPackage来写Hook代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.luoxposeddemo;

import android.util.Log;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class LuoHook implements IXposedHookLoadPackage {
    private final String TAG = "[LuoXposed]";

    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        if (lpparam.packageName.equals("com.example.luodst")) {
            Log.i(TAG, "Enter Hook");
            try {
                System.loadLibrary("luoxposeddemo");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
  1. 在项目的main目录下新建一个assets文件夹,分别指明Hook的入口类和准备注入的so文件
1
2
3
4
//xposed_init
com.example.luoxposeddemo.LuoHook
//native_init
libluoxposeddemo.so

注入的so文件

  1. 创建一个头文件(LSPlant.h)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
typedef int (*HookFunType)(void *func, void *replace, void **backup);
typedef int (*UnhookFunType)(void *func);
typedef void (*NativeOnModuleLoaded)(const char *name, void *handle);

typedef struct {
    uint32_t version;
    HookFunType hook_func;
    UnhookFunType unhook_func;
} NativeAPIEntries;

typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries);
  1. 在C层写Hook上述测试Apk的代码

native_init是Hook的入口函数,必须是导出函数.当有库文件加载时,它的返回值on_library_loaded回调函数,会被LSPosed 调用,故我们可以在这个函数中写针对目标Apk 指定so库函数的Hook代码.针对JNIEnv函数(比如GetMethodId)的Hook,我们可以在JNI_OnLoad函数中写Hook代码.

 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
42
43
#include <jni.h>
#include <string>
#include <dlfcn.h>
#include "LSPlant.h"
static HookFunType hook_func = nullptr;

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_luoxposeddemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

bool *(*back_check)(std::string strDst);
bool myCheck(std::string strDst){
    return true;
}

void on_library_loaded(const char *name, void *handle) {
    // hooks on `libluodst.so`
    if (std::string(name).compare("libluodst.so")) {
        void *target = dlsym(handle, "check");
        hook_func(target, (void *) myCheck, (void **) &back_check);
    }
}

extern "C" [[gnu::visibility("default")]] [[gnu::used]]
jint JNI_OnLoad(JavaVM *jvm, void*) {
    JNIEnv *env = nullptr;
    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);

    //hook_func((void *)env->functions->FindClass, (void *)fake_FindClass, (void **)&backup_FindClass);
    return JNI_VERSION_1_6;
}

extern "C" [[gnu::visibility("default")]] [[gnu::used]]
NativeOnModuleLoaded native_init(const NativeAPIEntries *entries) {
    hook_func = entries->hook_func;
    // system hooks
    //hook_func((void*) fopen, (void*) fake_fopen, (void**) &backup_fopen);
    return on_library_loaded;
}

参考链接

Native-Hook


相关内容

0%