当前位置: 首页 > ai >正文

怎么通过 jvmti 去 hook java 层函数

使用 JVMTI 手动实现 Android Java 函数 Hook

要通过 JVMTI 手动实现 Android Java 函数 Hook,需要编写 Native 层代码并注入到目标进程中。以下是详细步骤和示例:

一、核心实现原理

JVMTI 提供两种主要 Hook 方式:

  1. Method Entry/Exit 事件:监听方法调用前后的事件
  2. 字节码修改:在类加载时动态修改方法字节码

下面介绍如何通过 JVMTI Agent 实现这两种方式。

二、实现步骤

1. 创建 JVMTI Agent 项目

使用 Android NDK 创建一个 Native 库项目,包含以下文件:

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)add_library(jvmti_hookSHAREDjvmti_hook.cpp
)find_library(log-lib log)target_link_libraries(jvmti_hook${log-lib}
)

jvmti_hook.cpp

#include <jvmti.h>
#include <string>
#include <unordered_map>
#include <android/log.h>#define LOG_TAG "JVMTI_HOOK"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)// 方法ID到原始字节码的映射
std::unordered_map<jmethodID, unsigned char*> original_bytecode_map;// 类文件加载钩子回调
void JNICALL ClassFileLoadHook(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jclass class_being_redefined,jobject loader,const char* name,jobject protection_domain,jint class_data_len,const unsigned char* class_data,jint* new_class_data_len,unsigned char** new_class_data
) {// 过滤目标类std::string className(name);if (className.find("com/example/target/Class") != std::string::npos) {LOGD("Found target class: %s", name);// 这里可以使用 ASM 等库修改字节码// 简化示例:直接返回原始字节码*new_class_data_len = class_data_len;unsigned char* modified_data = new unsigned char[class_data_len];memcpy(modified_data, class_data, class_data_len);*new_class_data = modified_data;// 实际场景中,你需要:// 1. 解析 class_data (使用 ASM 或自定义解析器)// 2. 修改目标方法的字节码// 3. 返回修改后的字节码}
}// 方法进入回调
void JNICALL MethodEntry(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method) {char* method_name = nullptr;char* class_signature = nullptr;char* method_signature = nullptr;// 获取方法信息jvmti_env->GetMethodName(method, &method_name, &method_signature, nullptr);jclass declaring_class;jvmti_env->GetMethodDeclaringClass(method, &declaring_class);jvmti_env->GetClassSignature(declaring_class, &class_signature, nullptr);// 过滤目标方法if (std::string(class_signature).find("Lcom/example/target/Class;") != std::string::npos &&std::string(method_name) == "targetMethod") {LOGD("Entering target method: %s%s", class_signature, method_name);// 这里可以获取和修改局部变量// jvmti_env->GetLocalObject(...)// jvmti_env->SetLocalObject(...)}// 释放资源if (method_name) jvmti_env->Deallocate((unsigned char*)method_name);if (class_signature) jvmti_env->Deallocate((unsigned char*)class_signature);if (method_signature) jvmti_env->Deallocate((unsigned char*)method_signature);
}// Agent 初始化函数
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {jvmtiEnv *jvmti_env;jint result = vm->GetEnv((void **)&jvmti_env, JVMTI_VERSION_1_2);if (result != JNI_OK) {LOGE("ERROR: GetEnv failed, error code: %d", result);return JNI_ERR;}// 设置 JVMTI 能力jvmtiCapabilities caps;memset(&caps, 0, sizeof(caps));caps.can_generate_method_entry_events = 1;caps.can_generate_all_class_hook_events = 1;caps.can_generate_class_load_hook_events = 1;caps.can_get_bytecodes = 1;caps.can_modify_classes = 1;if (jvmti_env->AddCapabilities(&caps) != JNI_OK) {LOGE("ERROR: AddCapabilities failed");return JNI_ERR;}// 设置回调函数jvmtiEventCallbacks callbacks;memset(&callbacks, 0, sizeof(callbacks));callbacks.ClassFileLoadHook = &ClassFileLoadHook;callbacks.MethodEntry = &MethodEntry;if (jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)) != JNI_OK) {LOGE("ERROR: SetEventCallbacks failed");return JNI_ERR;}// 启用事件jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr);jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, nullptr);LOGD("JVMTI Agent loaded successfully");return JNI_OK;
}
2. 编译和打包

使用 NDK 编译生成 .so 库:

./gradlew assembleDebug
3. 注入 JVMTI Agent

有两种注入方式:

方式一:Root 设备注入
  1. .so 库推送到设备:

    adb push libs/armeabi-v7a/libjvmti_hook.so /data/local/tmp/
    
  2. 使用 ptrace 注入(需要 root 权限):

    # 使用 frida-gadget 或自定义 injector
    frida -U -f com.example.target -l /data/local/tmp/libjvmti_hook.so
    
方式二:非 Root 设备(需应用配合)
  1. 在应用代码中添加:
    public class MainActivity extends AppCompatActivity {static {System.loadLibrary("jvmti_hook");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 AgentinitJvmtiAgent();}private native void initJvmtiAgent();
    }
    
4. 高级 Hook:替换方法实现

如果需要完全替换方法实现,可以结合 JNI 和 JVMTI:

// 替换方法实现
void ReplaceMethodImplementation(jvmtiEnv* jvmti_env, JNIEnv* jni_env, jmethodID target_method) {// 创建一个新的本地方法jclass target_class;jvmti_env->GetMethodDeclaringClass(target_method, &target_class);// 注册新的本地方法JNINativeMethod native_method;native_method.name = "targetMethod"; // 目标方法名native_method.signature = "(Ljava/lang/String;I)V"; // 方法签名native_method.fnPtr = &NewMethodImplementation; // 新的方法实现jni_env->RegisterNatives(target_class, &native_method, 1);// 使类重新定义jvmtiClassDefinition class_def;class_def.class_ = target_class;class_def.class_byte_count = 0;class_def.class_bytes = nullptr;jvmti_env->RedefineClasses(1, &class_def);
}// 新的方法实现
JNIEXPORT void JNICALL NewMethodImplementation(JNIEnv* env, jobject obj, jstring str, jint num) {LOGD("Hooked method called with params: %s, %d", env->GetStringUTFChars(str, nullptr), num);// 执行自定义逻辑// ...// 可以选择调用原始方法// CallOriginalMethod(env, obj, str, num);
}

三、注意事项

  1. JVMTI 版本兼容性
    不同 Android 版本的 JVMTI 接口可能有差异,需要针对目标版本进行适配。

  2. 性能开销
    频繁的事件监听和字节码修改会影响应用性能,生产环境慎用。

  3. 线程安全
    JVMTI 回调可能在不同线程中执行,需要注意线程同步。

  4. 反调试措施
    目标应用可能检测 JVMTI Agent 的存在,可以结合代码混淆和反检测技术。

http://www.xdnf.cn/news/11802.html

相关文章:

  • 构建高效可靠的电商 API:设计原则与实践指南
  • PyTorch学习笔记 - 损失函数
  • unix/linux,sudo,其历史争议、兼容性、生态、未来展望
  • 如何有效删除 iPhone 上的所有内容?
  • 激光干涉仪:解锁协作机器人DD马达的精度密码
  • 前端工具库lodash与lodash-es区别详解
  • ES海量数据更新及导入导出备份
  • 设计模式之单例模式(二): 心得体会
  • UE 5 和simulink联合仿真,如果先在UE5这一端结束Play,过一段时间以后**Unreal Engine 5** 中会出现显存不足错误
  • 功能测试、性能测试、安全测试详解
  • 近端策略优化(PPO,Proximal Policy Optimization)
  • vue实现点击按钮input保持聚焦状态
  • Oracle实用参考(13)——Oracle for Linux静默安装(1)
  • springboot 微服务 根据tomcat maxthread 和 等待用户数量,达到阈值后,通知用户前面还有多少用户等待,请稍后重试
  • 低代码采购系统搭建:鲸采云+能源行业订单管理自动化案例
  • Electron打包前端和后端为exe
  • el-table 树形数据,子行数据可以异步加载
  • 破解HTTP无状态:基于Java的Session与Cookie协同工作指南
  • 618浴室柜推荐,小户型浴室柜怎么选才省心?
  • 江科大睡眠,停止,待机模式hal库实现
  • MySQL范式和反范式
  • Windows安装docker desktop
  • 【使用JAVA调用deepseek】实现自能回复
  • Devops自动化运维---py基础篇一
  • Appium如何支持ios真机测试
  • CppCon 2014 学习:Mixins for C++
  • 基于行为分析的下一代安全防御指南
  • webPack基本使用步骤
  • Cocos creator游戏开发面试题
  • Windows+Linux安装redis教程