Android Native forbids the use of private library

System private library refers to the Lib library stored under the android system / system/lib / and / vendor/lib, but there is no public API in Android NDK.

Starting from Android N (SDK > = 24), opening the system private library through dlopen, or depending on the system private library in the lib library, will generate exceptions, or even cause app crash. The details can be read Official documents Explain.

What is the impact of this change?

Once beautiful

In the past, at the ndk level, we could use some hack methods to get the private api of the system.

For example, if you want to use some internal symbols in the virtual machine, you can do this in the version below N

    void *handle = dlopen("libart.so", RTLD_NOW);
    
    void *originFunc = dlsym(handle, "_ZNK3art6Thread13DumpJavaStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE");

So you can get the function pointer of art::Thread::DumpJavaStack and call it happily.

A bolt from the blue

But after N,

void *handle = dlopen("libart.so", RTLD_NOW);
//  No problem, returned handle pointer.

void *originFunc = dlsym(handle, "_ZNK3art6Thread13DumpJavaStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE");
// Failure! The originFunc obtained is empty!

It's strange that we can get the handle pointer, which means that libart.so is found. But why can't the symbol of art::Thread::DumpJavaStack be found in libart.so?

Looking at the memory map, we found an interesting thing

7de5d4d000-7de5d4e000 r-xp 00000000 fe:00 774                            /system/fake-libs64/libart.so
7de5d4e000-7de5d4f000 r--p 00000000 fe:00 774                            /system/fake-libs64/libart.so
7de5d4f000-7de5d50000 rw-p 00001000 fe:00 774                            /system/fake-libs64/libart.so
... ...
7de6a04000-7de6feb000 r-xp 00000000 fe:00 1414                           /system/lib64/libart.so
7de6feb000-7de6ffa000 r--p 005e6000 fe:00 1414                           /system/lib64/libart.so
7de6ffa000-7de6ffd000 rw-p 005f5000 fe:00 1414                           /system/lib64/libart.so

No wonder, if we know that the dlopen parameter is libart.so, the system will first find / system / make libs64 / libart.so instead of / system/lib64/libart.so.

What the hell is / system/fake-libs64/libart.so? From the name, we know that he is a fake libart.
In the system source file Art / libart · fake / readme.md, we found the explanation for him,

A fake libart made to satisfy some misbehaving apps that will attempt to link
against libart.so.

This is a set specially designed to prevent you mischievous apps from doing something strange!

As long as your own lib library depends on libart.so or tries to open libart.so, when linker looks for libart.so, because the fake LIBS path is set at the front of the lookup path table, it will find / system / fake libs64 / libart.so first, rather than the real / system/lib64/libart.so.

Set the fake LIBS Code:

@ frameworks/base/core/java/android/app/LoadedApk.java

public static void makePaths(...) {
...
    // Add fake libs into the library search path if we target prior to N.
    if (aInfo.targetSdkVersion <= 23) {
        outLibPaths.add("/system/fake-libs" +
            (VMRuntime.is64BitAbi(aInfo.primaryCpuAbi) ? "64" : ""));
    }
...
}

The content of / system / make-libs64 / libart.so is basically empty (Art / libart_make / make. CC), so of course, there are no symbols in it~

Forced Entry

In this case, let's directly specify the total row of the absolute path of lib in dlopen? Like this:

void *handle = dlopen("/system/lib64/libart.so", RTLD_NOW);

Unfortunately, it made a mistake:

01-11 13:16:10.413 19869-19869/com.patch.demo E/linker: library "/system/lib64/libart.so" ("/system/lib64/libart.so")
needed or dlopened by "/data/app/com.patch.demo-1/lib/arm64/libbcpatch.so"
is not accessible for the namespace:
[name="classloader-namespace", ld_library_paths="",
default_library_paths="/data/app/com.patch.demo-1/lib/arm64:/system/fake-libs64:/data/app/com.patch.demo-1/base.apk!/lib/arm64-v8a",
permitted_paths="/data:/mnt/expand:/data/data/com.patch.demo"]

That is to say, the paths you are allowed to access (including LD Library paths, default library paths, and permitted paths) are only

/data/app/com.patch.demo-1/lib/arm64
/system/fake-libs64
/data/app/com.patch.demo-1/base.apk!/lib/arm64-v8a
/data
/mnt/expand
/data/data/com.patch.demo

So, it's not good to try to access libart.so under / system/lib64 /.

It's a magic one foot tall.

At this point, we know how Google can block access to system private libraries in ndk. In essence, a series of verification mechanisms are added to linker to make restrictions. Linker is the most basic lib library linker, and all link behaviors will be restricted.

A plot to gain time in order to complete defense

However, in Android N, you can specify the sdk of the APP as API level 23 or lower. Then, lib in the following gray list can still be used normally:

// TODO(dimitry): The grey-list is a workaround for http://b/26394120 ---
// gradually remove libraries from this list until it is gone.
static bool is_greylisted(const char* name, const soinfo* needed_by) {
  static const char* const kLibraryGreyList[] = {
    "libandroid_runtime.so",
    "libbinder.so",
    "libcrypto.so",
    "libcutils.so",
    "libexpat.so",
    "libgui.so",
    "libmedia.so",
    "libnativehelper.so",
    "libskia.so",
    "libssl.so",
    "libstagefright.so",
    "libsqlite.so",
    "libui.so",
    "libutils.so",
    "libvorbisidec.so",
    nullptr
  };

In this way, every time you use dlopen or link to the above lib, a warning will be printed out, and then the original function will still be executed normally.

At the same time, Google also announced that the support of these LIBS will be removed in future versions. So, this just provides a transition period for you to remove dependencies in your code as soon as possible.

It can be seen that in the near future, we will not be able to use the system's non-public symbols happily.

Highlight tight encirclement

So we really can't help it?

It's not absolute. Android only restricts dlopen, and we can access memory as we like:)

The method is to find the real starting position of libart.so through the memory mapping table:

7de6a04000-7de6feb000 r-xp 00000000 fe:00 1414                           /system/lib64/libart.so
7de6feb000-7de6ffa000 r--p 005e6000 fe:00 1414                           /system/lib64/libart.so
7de6ffa000-7de6ffd000 rw-p 005f5000 fe:00 1414                           /system/lib64/libart.so

Then, the elf format of libart.so is manually parsed at the start of the loading address to extract the location information of the required symbols. It is equivalent to your own implementation of linker's original search logic.

Of course, this implementation of traversal memory parsing elf is relatively complex. So this time, Google is blocking a wave of bottom hack s.

However, there are still many ways to circumvent this restriction on the Internet, and you can explore what you are interested in.

You can also come here to discuss technology with us

Tags: Android Linker Google SDK

Posted on Sun, 08 Mar 2020 19:47:52 -0700 by richardbotw