前言
使用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函数为例.
修改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" />
将XposedBridgeApi.jar放到项目中的app/libs目录下
配置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'
配置settings.gradle,在repositories中添加下面两个任意一个即可
1
2
maven { url ' https : //maven.aliyun.com/repository/public/' }
//maven { url "https://api.xposed.info/" }
新建一个类继承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 ();
}
}
}
}
在项目的main目录下新建一个assets 文件夹,分别指明Hook的入口类和准备注入的so文件
1
2
3
4
//xposed_init
com . example . luoxposeddemo . LuoHook
//native_init
libluoxposeddemo . so
创建一个头文件(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 );
在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