Hook的原理是修改java层的method属性,然后注册jni函数,但是实现起来还是有点复杂,具体看下面的函数。
int Hook(){
init();
void* handle = dlopen("/data/local/libTest.so",RTLD_NOW);
const char *dlopen_error = dlerror();
if(!handle){
DEBUG_PRINT("cannt load plugin :%s",dlopen_error);
return -1;
}
SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo");
const char *dlsym_error = dlerror();
if (dlsym_error) {
DEBUG_PRINT("Cannot load symbol 'getpHookInfo' :%s" , dlsym_error);
dlclose(handle);
return 1;
}
HookInfo *hookInfo;
setup(&hookInfo);
DEBUG_PRINT("Target Class:%s",hookInfo[0].tClazz);
DEBUG_PRINT("Target Method:%s",hookInfo[0].tMethod);
ClassMethodHook(hookInfo[0]);
return 0;
}
1. init初始化虚拟机环境
2. 加载要注册jni函数的so文件
3. 注册jni函数,并修改java层对应的method方法属性
一、init函数
void init()
{
g_bAttatedT = false;
g_JavaVM = android::AndroidRuntime::getJavaVM();
}
这里通过android运行环境获得虚拟机句柄或者称之为虚拟机指针,思考一下为何能够通过android::AndroidRuntime::getJavaVM();获取虚拟机指针,为什么正常情况下写的jni函数,此语句无法通过编译。
android系统在启动时会通过init.rc启动zygote进程,而zygote进行会初始化android运行时环境,并且初始化jvm相关的环境,而所有的app程序都是通过zygote fork出来的,相应的android运行时环境和jvm环境也会与zygote相同。
那么在任意一个app的native层,都可以通过android::AndroidRuntime::getJavaVM();获取jvm指针,navite层本来就是linux系统进程的概念,可以笼统的把jvm看做是linux进程中运行的一个线程,这么一想就通了。
二、so动态库加载
linux有通用的函数dlopen、dlsym、dlclose函数来处理so动态库的加载,这里也必然使用这几个函数来加载我们需要的so库。
dlopen就不多说了,看一下dlsym
SetupFunc setup = (SetupFunc)dlsym(handle,"getpHookInfo");
这里是获取so库中getpHookInfo符号的地址,也就是获取此函数的地址,那么SetupFunc setup怎么理解呢?如果C基础比较扎实的话,这不是个问题,看一下声明:typedef int (*SetupFunc)(HookInfo**);这里定义了一个函数指针,函数指针的行为为(*函数名)(参数);
HookInfo *hookInfo;
setup(&hookInfo);
这一步就是执行函数,或者说函数的调用。
三、java层函数的hook
bool ClassMethodHook(HookInfo info){
JNIEnv *jenv = GetEnv();
jclass clazzTarget = jenv->FindClass(info.tClazz);
if (ClearException(jenv)) {
DEBUG_PRINT("ClassMethodHook[Can't find class:%s in bootclassloader",info.tClazz);
clazzTarget = findAppClass(jenv,info.tClazz);
if(clazzTarget == NULL){
DEBUG_PRINT("%s","Error in findAppClass");
return false;
}
}
jmethodID method = jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig);
if(method==NULL){
DEBUG_PRINT("ClassMethodHook[Can't find method:%s",info.tMethod);
return false;
}
if(isArt()){
HookArtMethod(jenv,method);
}else{
HookDalvikMethod(method);
}
JNINativeMethod gMethod[] = {
{info.tMethod, info.tMeihodSig, info.handleFunc},
};
if(info.handleFunc != NULL){
if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) {
DEBUG_PRINT("err");
return false;
}
}
DetachCurrent();
return true;
}
这个函数是重点,这里如何hook以及如何在so中注册jni函数呢?
想要hook java层函数,先要获取此函数的Method指针,然后在对此指针进行操作。我们看一看如何获取Method句柄。
想要获取类的一些信息,比如想要获取和jvm进行沟通的渠道,这个渠道就是JNIEnv,jni句柄,
static JNIEnv *GetEnv()
{
int status;
JNIEnv *envnow = NULL;
status = g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);
if(status < 0)
{
status = g_JavaVM->AttachCurrentThread(&envnow, NULL);
if(status < 0)
{
return NULL;
}
g_bAttatedT = true;
}
return envnow;
}
通过jvm句柄获取g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4);这里有一个关键的处理就是绑定线程AttachCurrentThread(&envnow, NULL);
获取到JNIEnv后,便可以通过findClass获取类对象的句柄:jclass clazzTarget = jenv->FindClass(info.tClazz);
这是一个常用的方法,不多解。
jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig)
这句是从class对象获取method属性,然后修改此method对应的属性。
四、修改Method属性
bool HookDalvikMethod(jmethodID jmethod){
DEBUG_PRINT("HookDalvikMethod");
Method *method = (Method*)jmethod;
SET_METHOD_FLAG(method, ACC_NATIVE);
int argsSize = dvmComputeMethodArgsSize(method);
if (!dvmIsStaticMethod(method))
argsSize++;
method->registersSize = method->insSize = argsSize;
if (dvmIsNativeMethod(method)) {
method->nativeFunc = dvmResolveNativeMethod;
method->jniArgInfo = computeJniArgInfo(&method->prototype);
}
return true;
}
1. 设置method 为native属性:SET_METHOD_FLAG(method, ACC_NATIVE);
2. 计算method的参数个数,如果不是static,需要添加this参数
3. 设置nativeFunc地址函数
3. 设置jniArgInfo参数信息
这里重要的点是computeJniArgInfo(&method->prototype);来计算输入参数和返回类型。
下一篇会分析jni函数的调用逻辑。
分享到:
相关推荐
centos7.4+ nvidia-docker2 安装所需要的必备包之一 libnvidia-container-tools-1.0.2-1.x86_64.rpm ...nvidia-container-runtime-hook-1.4.0-2.x86_64.rpm nvidia-docker2-2.0.3-3.docker18.09.6.ce.noarch.rpm
hook-tar/apache-atlas-2.2.0-falcon-hook.tar.gz hook-tar/apache-atlas-2.2.0-hbase-hook.tar.gz hook-tar/apache-atlas-2.2.0-hive-hook.tar.gz hook-tar/apache-atlas-2.2.0-impala-hook.tar.gz hook-tar/apache...
atlas hive hook 资源包
atlas hive hook 已编译版本apache-atlas-2.1.0-hive-hook.tar.gz 可用于hive中进行数据抓取 和元数据信息管理
NVIDIA-DOCKER-18.06版本 nvidia-container-runtime-hook_1.4.0-1_amd64.deb
apache-atlas-2.1.0-hbase-hook.tar.gz--基于cdh6.3.1编译完成
离线安装包,亲测可用
apache-atlas-2.3.0-hive-hook.tar.gz Apache Atlas 框架是一组可扩展的核心基础治理服务,使企业能够有效且高效地满足 Hadoop 内的合规性要求,并允许与整个企业数据生态系统集成。这将通过使用规范和取证模型、...
apache-atlas-2.3.0-hbase-hook.tar.gz Apache Atlas 框架是一组可扩展的核心基础治理服务,使企业能够有效且高效地满足 Hadoop 内的合规性要求,并允许与整个企业数据生态系统集成。这将通过使用规范和取证模型、...
apache-atlas-2.2.0-falcon-hook.tar.gz
apache-atlas-2.2.0-sqoop-hook.tar.gz
apache-atlas-2.1.0-kafka-hook.tar.gz--基于cdh6.3.1编译完成
Atlas2.0 自己编译通过的包,独立打包 可以使用的,希望多大家是有用的
apache-atlas-2.2.0-hbase-hook.tar.gz
npm install --save react-modal-hook 用法 使用ModalProvider为您的应用程序提供模态上下文: import React from "react" ; import ReactDOM from "react-dom" ; import { ModalProvider } from "react-modal-hook...
apache-atlas-2.0.0-hive-hook ,亲测有用!
Laravel开发-laravel-git-hook Laravel Git钩子
apache-atlas-2.1.0-falcon-hook.tar.gz--基于cdh6.3.1编译完成
apache-atlas-2.2.0-kafka-hook.tar.gz
前端开源库-exit-hook退出钩子,当进程退出时运行一些代码