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

怎么通过 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.lryc.cn/news/2402385.html

相关文章:

  • 兰亭妙微 | 医疗软件的界面设计能有多专业?
  • 前端原生构建交互式进度步骤组件(Progress Steps)
  • 如何给windos11 扩大C盘容量
  • 【基于阿里云搭建数据仓库(离线)】Data Studio创建资源与函数
  • Linux_T(Sticky Bit)粘滞位详解
  • web3-以太坊智能合约基础(理解智能合约Solidity)
  • 高敏感应用如何保护自身不被逆向?iOS 安全加固策略与工具组合实战(含 Ipa Guard 等)
  • 【C++项目】负载均衡在线OJ系统-2
  • GC1809:高性能24bit/192kHz音频接收芯片解析
  • 2025年06月05日Github流行趋势
  • flask功能使用总结和完整示例
  • AWS 亚马逊 S3存储桶直传 前端demo 复制即可使用
  • DAY 15 复习日
  • Vue Router 导航方法完全指南
  • MidJourney入门学习
  • 2025最新Java日志框架深度解析:Log4j 2 vs Logback性能实测+企业级实战案例
  • 如何安全高效的文件管理?文件管理方法
  • 基于BI PaaS架构的衡石HENGSHI SENSE平台技术解析:重塑企业级数据分析基座
  • Hive中ORC存储格式的优化方法
  • 代码训练LeetCode(23)随机访问元素
  • 【R语言编程绘图-plotly】
  • float、double 这类 浮点数 相比,DECIMAL 是另一种完全不同的数值类型
  • 通信刚需,AI联手ethernet/ip转profinet网关打通工业技术难关
  • JavaEE->多线程:定时器
  • 6个月Python学习计划 Day 15 - 函数式编程、高阶函数、生成器/迭代器
  • <el-table>构建树形结构
  • linux——磁盘和文件系统管理
  • 云原生 DevOps 实践路线:构建敏捷、高效、可观测的交付体系
  • gateway 网关 路由新增 (已亲测)
  • ArcGIS Pro 3.4 二次开发 - 共享