首页
社区
课程
招聘
[原创]一种通用超简单的Android Java Native方法Hook,无需依赖Hook框架
发表于: 2021-3-2 12:20 17398

[原创]一种通用超简单的Android Java Native方法Hook,无需依赖Hook框架

2021-3-2 12:20
17398

目前 AndroidHook 的框架已经很多了,但是支持 Java Native 方法的 Hook 却很少,这些框架将 native 方法当普通方法 Hook,适配不同架构复杂等等。本文介绍一种 Android 版本通用的 Java Native Hook 方法并实现代码很少,下面进入我们的分析。

目前 native 方法只有两种方式

根据上面两种方式很自然想到了两种 Hook 方法

RegisterNatives 最新源码实现位置在 art/runtime/jni/jni_internal.cc,其源码如下:

这个方法主要进行各种验证并查找方法对应的 ArtMethod,其中 FastNative 在 Android8.0 以后已经采用注解的方式了,最终调用 class_linker->RegisterNative(soa.Self(), m, fnPtr) 来完成函数注册,接着分析

有关 JVMTI 大家可以网上搜索,通过它能做到很多黑科技并且这里也使用了它修改后的 new_native_method,因此通过 JVMTI 也能达到 Hook。 ,这里判断 CriticalNative 如果没有初始化类则先要初始化类,然后再注册。最终实现注册的是 method->SetEntryPointFromJni(new_native_method)

最终只是设置 ArtMethod 对象中的 jni 入口点指针为我们的注册函数,上面是主分支代码分析,Android 11 及以下都是调用的 ArtMethod::RegisterNative方法

这里可以看到在 Android 9 以上直接调用即可覆盖掉,Android 9 以下需要清理 FastNative 标志

经过上面分析要 Hook 则只需要调用 RegisterNatives 方法,而我们还要备份原方法方便后面可以调用,而原方法的地址在 ArtMethod 对象中的 jni 入口指针中保存,因此需要查找 ArtMethod,为了简单适配不同我们自己手动注册一个函数,然后再拿这个地址跟 ArtMethod 对象中去比较获取偏移量

Android 9 以下如果原方法是 FastNative 类型则还需要清除标志,因此还要查找 uint32_t accessFlags 成员的偏移,这里我们又采用查找的方式,来确定偏移,使用 uint32_t 对齐,我这里选择的是 0x109 标志也就是 public static native,这里最好要有 public 标志 0x1,因为正常指针都是 4/8 字节对齐的,这样避免误判

fake-linker 集成 JNIJava native 函数 HookLD_PRELOAD 模式的 PLT HookAndroid 7以上绕过命名空间限制等等

数据滤镜 用于分析恶意软件,提供高度自由的数据过滤,文件重定向,maps 文件过滤,动态符号查找过滤等等

点击 这里

extern "C"
JNIEXPORT void JNICALL Java_com_sanfengandroid_fakelinker_FakeLinker_setLogLevel(JNIEnv *env, jclass clazz, jint level) {
    g_log_level = level;
}
extern "C"
JNIEXPORT void JNICALL Java_com_sanfengandroid_fakelinker_FakeLinker_setLogLevel(JNIEnv *env, jclass clazz, jint level) {
    g_log_level = level;
}
static jint RegisterNatives(JNIEnv* env,
                            jclass java_class,
                            const JNINativeMethod* methods,
                            jint method_count) {
  if (UNLIKELY(method_count < 0)) {
    JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
                                    method_count);
    return JNI_ERR;  // Not reached except in unit tests.
  }
  CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  ScopedObjectAccess soa(env);
  StackHandleScope<1> hs(soa.Self());
  Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
  if (UNLIKELY(method_count == 0)) {
    LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
        << c->PrettyDescriptor();
    return JNI_OK;
  }
  CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
  for (jint i = 0; i < method_count; ++i) {
    const char* name = methods[i].name;
    const char* sig = methods[i].signature;
    const void* fnPtr = methods[i].fnPtr;
    if (UNLIKELY(name == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
      return JNI_ERR;
    } else if (UNLIKELY(sig == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
      return JNI_ERR;
    } else if (UNLIKELY(fnPtr == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
      return JNI_ERR;
    }
    bool is_fast = false;
 
    if (*sig == '!') {
      is_fast = true;
      ++sig;
    }
    // 上面是一些参数验证
    ArtMethod* m = nullptr;
    bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
    for (ObjPtr<mirror::Class> current_class = c.Get();
        current_class != nullptr;
        current_class = current_class->GetSuperClass()) {
      // 查询方法对应的 ArtMethod 对象
      m = FindMethod<true>(current_class, name, sig);
      if (m != nullptr) {
        break;
      }
 
      // Search again comparing to all methods, to find non-native methods that match.
      m = FindMethod<false>(current_class, name, sig);
      if (m != nullptr) {
        break;
      }
 
      if (warn_on_going_to_parent) {
        LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
                    << "This is slow, consider changing your RegisterNatives calls.";
        warn_on_going_to_parent = false;
      }
    }
 
    if (m == nullptr) {
      c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
      LOG(ERROR)
          << "Failed to register native method "
          << c->PrettyDescriptor() << "." << name << sig << " in "
          << c->GetDexCache()->GetLocation()->ToModifiedUtf8();
      ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
      return JNI_ERR;
    } else if (!m->IsNative()) {
      // 非 native 方法是不能注册的
      LOG(ERROR)
          << "Failed to register non-native method "
          << c->PrettyDescriptor() << "." << name << sig
          << " as native";
      ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
      return JNI_ERR;
    }
 
    VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]";
 
    if (UNLIKELY(is_fast)) {
      LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
      is_fast = false;
      // TODO: make this a hard register error in the future.
    }
    // 最终调用 class_linker->RegisterNative 来实际进行注册
    const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr);
    UNUSED(final_function_ptr);
  }
  return JNI_OK;
}
static jint RegisterNatives(JNIEnv* env,
                            jclass java_class,
                            const JNINativeMethod* methods,
                            jint method_count) {
  if (UNLIKELY(method_count < 0)) {
    JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
                                    method_count);
    return JNI_ERR;  // Not reached except in unit tests.
  }
  CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  ScopedObjectAccess soa(env);
  StackHandleScope<1> hs(soa.Self());
  Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
  if (UNLIKELY(method_count == 0)) {
    LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
        << c->PrettyDescriptor();
    return JNI_OK;
  }
  CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
  for (jint i = 0; i < method_count; ++i) {
    const char* name = methods[i].name;
    const char* sig = methods[i].signature;
    const void* fnPtr = methods[i].fnPtr;
    if (UNLIKELY(name == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
      return JNI_ERR;
    } else if (UNLIKELY(sig == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
      return JNI_ERR;
    } else if (UNLIKELY(fnPtr == nullptr)) {
      ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
      return JNI_ERR;
    }
    bool is_fast = false;
 
    if (*sig == '!') {
      is_fast = true;
      ++sig;
    }
    // 上面是一些参数验证
    ArtMethod* m = nullptr;
    bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
    for (ObjPtr<mirror::Class> current_class = c.Get();
        current_class != nullptr;
        current_class = current_class->GetSuperClass()) {
      // 查询方法对应的 ArtMethod 对象
      m = FindMethod<true>(current_class, name, sig);
      if (m != nullptr) {
        break;
      }
 
      // Search again comparing to all methods, to find non-native methods that match.
      m = FindMethod<false>(current_class, name, sig);
      if (m != nullptr) {
        break;
      }
 
      if (warn_on_going_to_parent) {
        LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. "
                    << "This is slow, consider changing your RegisterNatives calls.";
        warn_on_going_to_parent = false;
      }
    }
 
    if (m == nullptr) {
      c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
      LOG(ERROR)
          << "Failed to register native method "
          << c->PrettyDescriptor() << "." << name << sig << " in "
          << c->GetDexCache()->GetLocation()->ToModifiedUtf8();
      ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
      return JNI_ERR;
    } else if (!m->IsNative()) {
      // 非 native 方法是不能注册的
      LOG(ERROR)
          << "Failed to register non-native method "
          << c->PrettyDescriptor() << "." << name << sig
          << " as native";
      ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
      return JNI_ERR;
    }
 
    VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]";
 
    if (UNLIKELY(is_fast)) {
      LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
      is_fast = false;
      // TODO: make this a hard register error in the future.
    }
    // 最终调用 class_linker->RegisterNative 来实际进行注册
    const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr);
    UNUSED(final_function_ptr);
  }
  return JNI_OK;
}
const void* ClassLinker::RegisterNative(
    Thread* self, ArtMethod* method, const void* native_method) {
  CHECK(method->IsNative()) << method->PrettyMethod();
  CHECK(native_method != nullptr) << method->PrettyMethod();
  void* new_native_method = nullptr;
  Runtime* runtime = Runtime::Current();
  // 这里 JVMTI 响应注册事件
  runtime->GetRuntimeCallbacks()->RegisterNativeMethod(method,
                                                       native_method,
                                                       /*out*/&new_native_method);
  if (method->IsCriticalNative()) {
    MutexLock lock(self, critical_native_code_with_clinit_check_lock_);
    // Remove old registered method if any.
    auto it = critical_native_code_with_clinit_check_.find(method);
    if (it != critical_native_code_with_clinit_check_.end()) {
      critical_native_code_with_clinit_check_.erase(it);
    }
    // To ensure correct memory visibility, we need the class to be visibly
    // initialized before we can set the JNI entrypoint.
    if (method->GetDeclaringClass()->IsVisiblyInitialized()) {
      method->SetEntryPointFromJni(new_native_method);
    } else {
      critical_native_code_with_clinit_check_.emplace(method, new_native_method);
    }
  } else {
    method->SetEntryPointFromJni(new_native_method);
  }
  return new_native_method;
}
const void* ClassLinker::RegisterNative(
    Thread* self, ArtMethod* method, const void* native_method) {
  CHECK(method->IsNative()) << method->PrettyMethod();
  CHECK(native_method != nullptr) << method->PrettyMethod();
  void* new_native_method = nullptr;
  Runtime* runtime = Runtime::Current();
  // 这里 JVMTI 响应注册事件
  runtime->GetRuntimeCallbacks()->RegisterNativeMethod(method,
                                                       native_method,
                                                       /*out*/&new_native_method);
  if (method->IsCriticalNative()) {
    MutexLock lock(self, critical_native_code_with_clinit_check_lock_);
    // Remove old registered method if any.
    auto it = critical_native_code_with_clinit_check_.find(method);
    if (it != critical_native_code_with_clinit_check_.end()) {
      critical_native_code_with_clinit_check_.erase(it);
    }
    // To ensure correct memory visibility, we need the class to be visibly
    // initialized before we can set the JNI entrypoint.
    if (method->GetDeclaringClass()->IsVisiblyInitialized()) {
      method->SetEntryPointFromJni(new_native_method);
    } else {
      critical_native_code_with_clinit_check_.emplace(method, new_native_method);
    }
  } else {
    method->SetEntryPointFromJni(new_native_method);
  }
  return new_native_method;
}
void SetEntryPointFromJni(const void* entrypoint)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // The resolution method also has a JNI entrypoint for direct calls from
  // compiled code to the JNI dlsym lookup stub for @CriticalNative.
  DCHECK(IsNative() || IsRuntimeMethod());
  SetEntryPointFromJniPtrSize(entrypoint, kRuntimePointerSize);
}
void SetEntryPointFromJni(const void* entrypoint)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // The resolution method also has a JNI entrypoint for direct calls from
  // compiled code to the JNI dlsym lookup stub for @CriticalNative.
  DCHECK(IsNative() || IsRuntimeMethod());
  SetEntryPointFromJniPtrSize(entrypoint, kRuntimePointerSize);
}
// Android 9 ~ 11
const void* ArtMethod::RegisterNative(const void* native_method) {
  CHECK(IsNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}
// Android 9 以下
const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
  CHECK(IsNative()) << PrettyMethod();
  // 多了一个 FastNative 检测,在已经注册为 FastNative 后不能再次注册
  CHECK(!IsFastNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  if (is_fast) {
    AddAccessFlags(kAccFastNative);
  }
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}
// Android 9 ~ 11
const void* ArtMethod::RegisterNative(const void* native_method) {
  CHECK(IsNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}
// Android 9 以下
const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
  CHECK(IsNative()) << PrettyMethod();
  // 多了一个 FastNative 检测,在已经注册为 FastNative 后不能再次注册
  CHECK(!IsFastNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (3)
雪    币: 2904
活跃值: (1337)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-3-28 17:54
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
java native方法hook本来就不需要框架。即便用框架最多也只是加载so而已。
2021-3-28 18:03
0
雪    币: 245
活跃值: (242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
太麻烦
2022-9-9 14:59
1
游客
登录 | 注册 方可回帖
返回
//