├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── jnihook │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── test_dispatch_table.c │ │ ├── test_jni_hook.c │ │ └── test_jni_hook.h │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── jnihook │ │ │ ├── DispatchTableHook.kt │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_dispatch_table_hook.xml │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── example │ └── jnihook │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jniTest ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ └── test_jni.c │ └── java │ │ └── com │ │ └── example │ │ └── jnitest │ │ └── NativeLib.kt │ └── test │ └── java │ └── com │ └── example │ └── jnitest │ └── ExampleUnitTest.kt ├── jnihook ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── dispatchtable_hook.c │ │ ├── dl_symbol_search.c │ │ ├── dl_symbol_search.h │ │ ├── include │ │ │ ├── dispatchtable_hook.h │ │ │ └── jni_hook.h │ │ └── jni_hook.c │ └── java │ │ └── com │ │ └── pika │ │ └── jnihook │ │ └── JniHook.kt │ └── test │ └── java │ └── com │ └── pika │ └── jnihook │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JniHook 2 | 一个简单的工具库,专门处理Jni 函数调用Hook 3 | 4 | 原理讲解:https://juejin.cn/post/7268894037464367140 5 | 6 | # 使用指南 7 | ## 开启prefad 8 | ``` 9 | build.gradle 中开启prefab 10 | buildFeatures { 11 | prefab true 12 | } 13 | ``` 14 | ## 初始化hook 15 | ``` 16 | JniHook.jniHookInit() 17 | ``` 18 | 19 | ## hook jni函数 20 | 需要的时候调用hook_jni函数即可 21 | 22 | * env:jni 环境 23 | * method:java层中需要hook的jni函数,即Method 24 | * new_entrance:native层的代理函数指针 25 | * origin_entrance:原函数指针的指针(指针的地址) 26 | ``` 27 | int hook_jni(JNIEnv *env, jobject method, void *new_entrance, void **origin_entrance) 28 | ``` 29 | 返回值:1(hook 成功) 0(当前已经hook) -1(hook失败) -2(当前jni函数还没有注册,可以通过注册RegisterNatives函数监听,见set_register_natives_call) 30 | ## unhook jni函数 31 | 如果需要解除hook,调用unhook_jni 32 | * env:jni 环境 33 | * method:java层中hook的jni函数,即Method 34 | * origin_entrance:原函数指针 35 | ``` 36 | void unhook_jni(JNIEnv *env, jobject method, void *origin_entrance) 37 | ``` 38 | ## set_register_natives_call 39 | 当jni 函数还未被加载时,此时hook_jni会返回无效状态-2,因此可以监听RegisterNatives 函数调用进行查看是否有需要的jni函数正在被注册,注册之后hook_jni才会返回1 40 | ``` 41 | void set_register_natives_call(register_native_call call) 42 | ``` 43 | 44 | 45 | 46 | 47 | ## 项目层级介绍 48 | * **app下是使用例子** 49 | * **jnihook 是jnihook的核心实现** 50 | 51 | ## 环境准备 52 | 建议直接用最新的稳定版本Android Studio打开工程。目前项目已适配`Android Studio Arctic Fox | 2022.3.1` 53 | ### 54 | 55 | ## 感谢 56 | [btrace]([https://www.runoob.com](https://github.com/bytedance/btrace)https://github.com/bytedance/btrace) 57 | 58 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.jnihook' 8 | compileSdk 32 9 | 10 | packagingOptions { 11 | pickFirst '**/libxdl.so' 12 | } 13 | 14 | defaultConfig { 15 | applicationId "com.example.jnihook" 16 | minSdk 23 17 | targetSdk 32 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | externalNativeBuild { 23 | cmake { 24 | arguments '-DANDROID_STL=c++_shared' 25 | } 26 | } 27 | ndk { 28 | abiFilters 'armeabi-v7a', 'arm64-v8a' 29 | } 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | kotlinOptions { 43 | jvmTarget = '1.8' 44 | } 45 | externalNativeBuild { 46 | cmake { 47 | path file('src/main/cpp/CMakeLists.txt') 48 | version '3.18.1' 49 | } 50 | } 51 | buildFeatures { 52 | viewBinding true 53 | } 54 | buildFeatures { 55 | prefab true 56 | } 57 | } 58 | 59 | dependencies { 60 | 61 | implementation 'androidx.core:core-ktx:1.7.0' 62 | implementation 'androidx.appcompat:appcompat:1.5.1' 63 | implementation 'com.google.android.material:material:1.5.0' 64 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 65 | implementation project(":jnihook") 66 | implementation project(":jniTest") 67 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/jnihook/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.jnihook 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.* 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.example.jnihook", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.18.1) 7 | 8 | # Declares and names the project. 9 | 10 | project("jnihooktest") 11 | 12 | # Creates and names a library, sets it as either STATIC 13 | # or SHARED, and provides the relative paths to its source code. 14 | # You can define multiple libraries, and CMake builds them for you. 15 | # Gradle automatically packages shared libraries with your APK. 16 | 17 | add_library( # Sets the name of the library. 18 | jnihooktest 19 | 20 | # Sets the library as a shared library. 21 | SHARED 22 | 23 | # Provides a relative path to your source file(s). 24 | test_jni_hook.c 25 | test_dispatch_table.c 26 | ) 27 | 28 | # Searches for a specified prebuilt library and stores the path as a 29 | # variable. Because CMake includes system libraries in the search path by 30 | # default, you only need to specify the name of the public NDK library 31 | # you want to add. CMake verifies that the library exists before 32 | # completing its build. 33 | 34 | find_library( # Sets the name of the path variable. 35 | log-lib 36 | 37 | # Specifies the name of the NDK library that 38 | # you want CMake to locate. 39 | log) 40 | 41 | # Specifies libraries CMake should link to your target library. You 42 | # can link multiple libraries, such as libraries you define in this 43 | # build script, prebuilt third-party libraries, or system libraries. 44 | 45 | target_link_libraries( # Specifies the target library. 46 | jnihooktest 47 | 48 | # Links the target library to the log library 49 | # included in the NDK. 50 | ${log-lib}) 51 | 52 | find_package(jnihook REQUIRED CONFIG) 53 | #### 54 | target_link_libraries(jnihooktest jnihook::jnihook) -------------------------------------------------------------------------------- /app/src/main/cpp/test_dispatch_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | static void *origin_calloc; 7 | 8 | static void *my_calloc(size_t __item_count, size_t __item_size) { 9 | __android_log_print(ANDROID_LOG_ERROR, "hello", "calloc hook %zu %zu", __item_count, 10 | __item_size); 11 | MallocCalloc c = (MallocCalloc) origin_calloc; 12 | return c(__item_count, __item_size); 13 | } 14 | 15 | JNIEXPORT void JNICALL 16 | Java_com_example_jnihook_DispatchTableHook_testDispatchTableHook(JNIEnv *env, jobject thiz) { 17 | init_dispatch_table(); 18 | dispatch_table_hook(CALLOC, my_calloc, &origin_calloc); 19 | calloc(1,4); 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/cpp/test_jni_hook.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "test_jni_hook.h" 6 | #include "jni_hook.h" 7 | 8 | 9 | // 定义原始函数 10 | static void (*test_jni_original)(JNIEnv *, jobject); 11 | 12 | static void test_jni_hook_proxy(JNIEnv *env, jobject java_this) { 13 | // 先走到代理函数,然后走到原函数 14 | __android_log_print(ANDROID_LOG_ERROR, "hello", "%s", "test_jni_hook_proxy"); 15 | 16 | test_jni_original(env, java_this); 17 | } 18 | 19 | 20 | JNIEXPORT void JNICALL 21 | Java_com_example_jnihook_MainActivity_hooktest(JNIEnv *env, jobject thiz, jobject method) { 22 | int result = hook_jni(env, method, (void *) test_jni_hook_proxy, (void **) &test_jni_original); 23 | __android_log_print(ANDROID_LOG_ERROR, "hello", "jni hook result %d", result); 24 | } 25 | 26 | JNIEXPORT void JNICALL 27 | Java_com_example_jnihook_MainActivity_unhooktest(JNIEnv *env, jobject thiz, jobject method) { 28 | unhook_jni(env, method, test_jni_original); 29 | 30 | } 31 | 32 | 33 | static void 34 | test_register_native(JNIEnv *env, jclass c, const JNINativeMethod *methods, jint nMethods) { 35 | __android_log_print(ANDROID_LOG_ERROR, "hello", "当前so进行了jni方法注册 %p", (*methods).fnPtr); 36 | } 37 | 38 | JNIEXPORT void JNICALL 39 | Java_com_example_jnihook_MainActivity_testRegisterNative(JNIEnv *env, jobject thiz) { 40 | set_register_natives_call(test_register_native); 41 | } -------------------------------------------------------------------------------- /app/src/main/cpp/test_jni_hook.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/jnihook/DispatchTableHook.kt: -------------------------------------------------------------------------------- 1 | package com.example.jnihook 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.util.Log 7 | import androidx.appcompat.app.AppCompatActivity 8 | import com.example.jnihook.databinding.ActivityDispatchTableHookBinding 9 | 10 | class DispatchTableHook : AppCompatActivity() { 11 | private lateinit var binding: ActivityDispatchTableHookBinding 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | binding = ActivityDispatchTableHookBinding.inflate(layoutInflater) 15 | setContentView(binding.root) 16 | binding.btnTest.setOnClickListener { 17 | testDispatchTableHook() 18 | } 19 | } 20 | 21 | external fun testDispatchTableHook() 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/jnihook/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.jnihook 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.example.jnihook.databinding.ActivityMainBinding 7 | import com.example.jnitest.NativeLib 8 | import com.pika.jnihook.JniHook 9 | import java.lang.reflect.Method 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | private lateinit var binding: ActivityMainBinding 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | binding = ActivityMainBinding.inflate(layoutInflater) 19 | setContentView(binding.root) 20 | // 初始化hook 21 | JniHook.jniHookInit() 22 | 23 | val hookMethod = NativeLib::class.java.getDeclaredMethod("testJNI") 24 | val nativeTest = NativeLib() 25 | 26 | binding.originalCall.setOnClickListener { 27 | // 正常调用函数 28 | nativeTest.initSo() 29 | nativeTest.testJNI() 30 | } 31 | binding.jnihook.setOnClickListener { 32 | hooktest(hookMethod) 33 | } 34 | 35 | binding.unhook.setOnClickListener { 36 | unhooktest(hookMethod) 37 | } 38 | 39 | binding.registerNativeCall.setOnClickListener { 40 | testRegisterNative() 41 | } 42 | 43 | binding.entry.setOnClickListener { 44 | startActivity(Intent(this, DispatchTableHook::class.java)) 45 | } 46 | 47 | } 48 | 49 | // 需要hook的函数 50 | companion object { 51 | init { 52 | System.loadLibrary("jnihooktest") 53 | } 54 | } 55 | 56 | // 替换的函数 57 | external fun hooktest(method: Method) 58 | external fun unhooktest(method: Method) 59 | 60 | external fun testRegisterNative() 61 | 62 | 63 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_dispatch_table_hook.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |