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%,毕竟安卓上碎片化严重