Android7之禁止Native 动态链接系统库

探讨Android7及以上版本中对于动态库私有函数调用的限制,并介绍三种绕过方法及其局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android7及以后的系统,限制了动态库私有函数调用,可以在linker.cpp找到dlopen的黑名单:

  

210static bool is_greylisted(const char* name, const soinfo* needed_by) {
211  static const char* const kLibraryGreyList[] = {
212    "libandroid_runtime.so",
213    "libbinder.so",
214    "libcrypto.so",
215    "libcutils.so",
216    "libexpat.so",
217    "libgui.so",
218    "libmedia.so",
219    "libnativehelper.so",
220    "libskia.so",
221    "libssl.so",
222    "libstagefright.so",
223    "libsqlite.so",
224    "libui.so",
225    "libutils.so",
226    "libvorbisidec.so",
227    nullptr
228  };
229
 
一旦调用dlopen打开这些文件就会失败,dlerror会提示****is not accessible for the namespace "classloader-namespace"这样的错误
再来看dlopen的实现,在do_dlopen函数中,返回的也不是6.0之前的soinfo*结构,而是一个handle:
2340void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
2341                  void* caller_addr) {
2342  soinfo* const caller = find_containing_library(caller_addr);
2343
2344  if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
2345    DL_ERR("invalid flags to dlopen: %x", flags);
2346    return nullptr;
2347  }
2348
2349  android_namespace_t* ns = get_caller_namespace(caller);
2350
2351  if (extinfo != nullptr) {
2352    if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
2353      DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
2354      return nullptr;
2355    }
2356
2357    if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 &&
2358        (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
2359      DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without "
2360          "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags);
2361      return nullptr;
2362    }
2363
2364    if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 &&
2365        (extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) {
2366      DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not "
2367             "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT");
2368      return nullptr;
2369    }
2370
2371    if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) {
2372      if (extinfo->library_namespace == nullptr) {
2373        DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null");
2374        return nullptr;
2375      }
2376      ns = extinfo->library_namespace;
2377    }
2378  }
2379
2380  std::string asan_name_holder;
2381
2382  const char* translated_name = name;
2383  if (g_is_asan) {
2384    if (file_is_in_dir(name, kSystemLibDir)) {
2385      asan_name_holder = std::string(kAsanSystemLibDir) + "/" + basename(name);
2386      if (file_exists(asan_name_holder.c_str())) {
2387        translated_name = asan_name_holder.c_str();
2388        PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
2389      }
2390    } else if (file_is_in_dir(name, kVendorLibDir)) {
2391      asan_name_holder = std::string(kAsanVendorLibDir) + "/" + basename(name);
2392      if (file_exists(asan_name_holder.c_str())) {
2393        translated_name = asan_name_holder.c_str();
2394        PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
2395      }
2396    }
2397  }
2398
2399  ProtectedDataGuard guard;
2400  soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
2401  if (si != nullptr) {
2402    si->call_constructors();
2403    return si->to_handle();
2404  }
2405
2406  return nullptr;
2407}

to_handle这里判断target_sdk_version>23,如果不成立则返回soinfo*结构,如果成立就返回一个随机数
同时用map结构将随机数与真正的soinfo*关联起来。没有方法能找到这个g_soinfo_handles_map
3468void* soinfo::to_handle() {
3469  if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) {
3470    return this;
3471  }
3472
3473  return reinterpret_cast<void*>(get_handle());
3474}
3462uintptr_t soinfo::get_handle() const {
3463  CHECK(has_min_version(3));
3464  CHECK(handle_ != 0);
3465  return handle_;
3466}
3476void soinfo::generate_handle() {
3477  CHECK(has_min_version(3));
3478  CHECK(handle_ == 0); // Make sure this is the first call
3479
3480  // Make sure the handle is unique and does not collide
3481  // with special values which are RTLD_DEFAULT and RTLD_NEXT.
3482  do {
3483    arc4random_buf(&handle_, sizeof(handle_));
3484    // the least significant bit for the handle is always 1
3485    // making it easy to test the type of handle passed to
3486    // dl* functions.
3487    handle_ = handle_ | 1;
3488  } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
3489           handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
3490           g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
3491
3492  g_soinfo_handles_map[handle_] = this;
3493}

因此新版dlopen存在2个问题:
①.dlopen返回值不再是soinfo*结构指针
②.dlopen不能打开黑名单内的so模块
 
有以下绕过机制:
1.自己实现dlopen
2.修改内存,将黑名单so文件从linker数据区抹除(只能破解②)
3.做深度的函数指令匹配,找到g_soinfo_handles_map(这种方式兼容性是很大问题)
无论哪种方式,兼容性都不会达到99%,毕竟安卓上碎片化严重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值