白云点缀的蓝 发表于 2025-3-28 12:07

安卓9.0loadLibrary源码分析

本帖最后由 白云点缀的蓝 于 2025-3-30 08:35 编辑

> 安卓源代码来自如下网站
> http://androidxref.com/
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/90c6ab02b404475e8fcd65d50b99ea6b.png#pic_center)
> 我就选最新的9.0系统分析了Pie - 9.0.0_r3
> 这里搜索LoadLibrary,右边select all 勾选上
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/590c1ac4f95a4cc483325d4d8b132039.png#pic_center)
> 然后点击Search,可以看到相关当前方法的调用。
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/bb43164ca6cb4f0cac7b536b74d17676.png#pic_center)
> 在这之前先说一下LoadLibrary方法的用法
> LoadLibrary来自System对象,所以调用时,
> 需要前面加上System,由于是直接调用的,
> 因此此对象不需要实例化,直接System.loadlibrary,
> loadlibrary方法会专门调用apk包中lib文件夹下的so文件,
> 下面拿一个安卓apk做示范,
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f3d1f2c0a0c64ca38a488cc7dfd6adb0.png#pic_center)
> 这里我用的是360压缩直接打开的,
> apk文件结构如下
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2105c2e80998443183d82ecd375865c1.png#pic_center)
> 继续回到安卓系统源码上,搜索后,一个一个看,
> 看到类似的函数声明
> 此函数返回值为万能指针,能存放所有数据类型的空间,
> 第一个方法数据类型为字符型,是调用LoadLibrary函数的
> so文件路径,在调用loadLibrary函数时安卓端省略了路径,
> 省略了lib前缀,省略了.so后缀,例如libLanAn.so
> 只需要输入System.loadLibrary(“LanAn”),即可,
>
> ```c
> 193void* (*loadLibrary)(const char* libpath, int flag);
> 194
> 195// Get a native bridge trampoline for specified native method. The trampoline has same
> 196// sigature as the native method.
> 197//
> 198// Parameters:
> 199//   handle the handle returned from loadLibrary
> 200//   shorty short descriptor of native method
> 201//   len length of shorty
> 202// Returns:
> 203//   address of trampoline if successful, otherwise NULL
> ```
> 源码跟踪示例
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ca34f603c0004f4aaedfffd0bc9597c6.png#pic_center)
>
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/8228638f03c44ef1bcd14aa00a6f4b84.png#pic_center)
> 点进去可以看到如下函数声明,这里只有声明
> 因此还需继续搜索,
>
> ```c
>void* (*loadLibrary)(const char* libpath, int flag);
> ```
> 可以试试搜索 void* loadLibrary这个函数
> 如下是搜索这个函数找到的结果
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ab8f4557aea046a895cd25d3792198f9.png#pic_center)
>
> ```c
> 30void *loadLibrary(const char *(&names), const char *mustContainSymbol = nullptr)
> 31{
> 32        for(int i = 0; i < n; i++)
> 33        {
> 34                void *library = getLibraryHandle(names);
> 35
> 36                if(library)
> 37                {
> 38                        if(!mustContainSymbol || getProcAddress(library, mustContainSymbol))
> 39                        {
> 40                                return library;
> 41                        }
> 42
> 43                        freeLibrary(library);
> 44                }
> 45        }
> 46
> 47        for(int i = 0; i < n; i++)
> 48        {
> 49                void *library = loadLibrary(names);
> 50
> 51                if(library)
> 52                {
> 53                        if(!mustContainSymbol || getProcAddress(library, mustContainSymbol))
> 54                        {
> 55                                return library;
> 56                        }
> 57
> 58                        freeLibrary(library);
> 59                }
> 60        }
> 61
> 62        return nullptr;
> 63}
> ```
> 继续跟踪如下代码
> 代码意思是获取so文件句柄,没有句柄无法操作so文件
>
> ```c
> void *library = getLibraryHandle(names);
> ```
> 如下是此函数getLibraryHandle的声明
>
> ```c
> 24void *getLibraryHandle(const char *path);
> ```
> 下面是关键代码
>
> ```c
> 65#if defined(_WIN32)
> 66        inline void *loadLibrary(const char *path)
> 67        {
> 68                return (void*)LoadLibrary(path);
> 69        }
> 70
> 71        inline void *getLibraryHandle(const char *path)
> 72        {
> 73                HMODULE module = NULL;
> 74                GetModuleHandleEx(0, path, &module);
> 75                return (void*)module;
> 76        }
> 77
> 78        inline void freeLibrary(void *library)
> 79        {
> 80                FreeLibrary((HMODULE)library);
> 81        }
> 82
> 83        inline void *getProcAddress(void *library, const char *name)
> 84        {
> 85                return (void*)GetProcAddress((HMODULE)library, name);
> 86        }
> 87#else
> 88        inline void *loadLibrary(const char *path)
> 89        {
> 90                return dlopen(path, RTLD_LAZY | RTLD_LOCAL);
> 91        }
> 92
> 93        inline void *getLibraryHandle(const char *path)
> 94        {
> 95                #ifdef __ANDROID__
> 96                        // bionic doesn't support RTLD_NOLOAD before L
> 97                        return dlopen(path, RTLD_NOW | RTLD_LOCAL);
> 98                #else
> 99                        void *resident = dlopen(path, RTLD_LAZY | RTLD_NOLOAD | RTLD_LOCAL);
> 100
> 101                        if(resident)
> 102                        {
> 103                                return dlopen(path, RTLD_LAZY | RTLD_LOCAL);   // Increment reference count
> 104                        }
> 105
> 106                        return nullptr;
> 107                #endif
> 108        }
> 109
> 110        inline void freeLibrary(void *library)
> 111        {
> 112                if(library)
> 113                {
> 114                        dlclose(library);
> 115                }
> 116        }
> 117
> 118        inline void *getProcAddress(void *library, const char *name)
> 119        {
> 120                void *symbol = dlsym(library, name);
> 121
> 122                if(!symbol)
> 123                {
> 124                        const char *reason = dlerror();   // Silence the error
> 125                        (void)reason;
> 126                }
> 127
> 128                return symbol;
> 129        }
> 130#endif
> ```
> 继续搜寻,发现该函数的实现
>
> ```c
> 93        inline void *getLibraryHandle(const char *path)
> 94        {
> 95                #ifdef __ANDROID__
> 96                        // bionic doesn't support RTLD_NOLOAD before L
> 97                        return dlopen(path, RTLD_NOW | RTLD_LOCAL);
> 98                #else
> 99                        void *resident = dlopen(path, RTLD_LAZY | RTLD_NOLOAD | RTLD_LOCAL);
> 100
> 101                        if(resident)
> 102                        {
> 103                                return dlopen(path, RTLD_LAZY | RTLD_LOCAL);   // Increment reference count
> 104                        }
> 105
> 106                        return nullptr;
> 107                #endif
> 108        }
> 109
> ```
> 如下代码打开了so文件,并且返回了文件句柄
>
> ```c
> return dlopen(path, RTLD_NOW | RTLD_LOCAL);
> ```
> 继续跟踪dlopen函数
>
> ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d41619f8a37c48efacf7d9cd8e1e2fed.png#pic_center)
> 如下是dlopen函数的实现
>
> ```c
> void *dlopen(char *filename, int flags)
> ```
> ```c
> 70/* load a dynamic-link library and return handle */
> 71void *dlopen(char *filename, int flags)
> 72{
> 73    HMODULE hm;
> 74    DLLchain tmp;
> 75    char err;
> 76    char *errtxt;
> 77    int rc = 0, set_chain = 0;
> 78
> 79    for (tmp = dlload; tmp; tmp = tmp->next)
> 80      if (strnicmp(tmp->name, filename, 999) == 0)
> 81            break;
> 82
> 83    if (!tmp)
> 84    {
> 85      tmp = (DLLchain) malloc(sizeof(tDLLchain));
> 86      if (!tmp)
> 87            goto nomem;
> 88      tmp->name = strdup(filename);
> 89      tmp->next = dlload;
> 90      set_chain = 1;
> 91    }
> 92
> 93    switch (rc = DosLoadModule((PSZ)&err, sizeof(err), filename, &hm))
> 94    {
> 95      case NO_ERROR:
> 96            tmp->handle = hm;
> 97            if (set_chain)
> 98            {
> 99                do
> 100                  last_id++;
> 101                while ((last_id == 0) || (find_id(last_id)));
> 102                tmp->id = last_id;
> 103                dlload = tmp;
> 104            }
> 105            return tmp->id;
> 106      case ERROR_FILE_NOT_FOUND:
> 107      case ERROR_PATH_NOT_FOUND:
> 108            errtxt = "module `%s' not found";
> 109            break;
> 110      case ERROR_TOO_MANY_OPEN_FILES:
> 111      case ERROR_NOT_ENOUGH_MEMORY:
> 112      case ERROR_SHARING_BUFFER_EXCEEDED:
> 113nomem:
> 114            errtxt = "out of system resources";
> 115            break;
> 116      case ERROR_ACCESS_DENIED:
> 117            errtxt = "access denied";
> 118            break;
> 119      case ERROR_BAD_FORMAT:
> 120      case ERROR_INVALID_SEGMENT_NUMBER:
> 121      case ERROR_INVALID_ORDINAL:
> 122      case ERROR_INVALID_MODULETYPE:
> 123      case ERROR_INVALID_EXE_SIGNATURE:
> 124      case ERROR_EXE_MARKED_INVALID:
> 125      case ERROR_ITERATED_DATA_EXCEEDS_64K:
> 126      case ERROR_INVALID_MINALLOCSIZE:
> 127      case ERROR_INVALID_SEGDPL:
> 128      case ERROR_AUTODATASEG_EXCEEDS_64K:
> 129      case ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT:
> 130            errtxt = "invalid module format";
> 131            break;
> 132      case ERROR_INVALID_NAME:
> 133            errtxt = "filename doesn't match module name";
> 134            break;
> 135      case ERROR_SHARING_VIOLATION:
> 136      case ERROR_LOCK_VIOLATION:
> 137            errtxt = "sharing violation";
> 138            break;
> 139      case ERROR_INIT_ROUTINE_FAILED:
> 140            errtxt = "module initialization failed";
> 141            break;
> 142      default:
> 143            errtxt = "cause `%s', error code = %d";
> 144            break;
> 145    }
> 146    snprintf(dlerr, sizeof(dlerr), errtxt, &err, rc);
> 147    if (tmp)
> 148    {
> 149      if (tmp->name)
> 150            free(tmp->name);
> 151      free(tmp);
> 152    }
> 153    return 0;
> 154}
> ```
> 这里看关键代码
> 这里选择跟踪函数参数,filename为so的文件名字,
> 也是dlopen函数的参数,
> 如下代码在对比文件名字
>
> ```c
> 80      if (strnicmp(tmp->name, filename, 999) == 0)
> 81            break;
> ```
> 继续跟踪
> 这里关键代码为
>
> ```c
> 88      tmp->name = strdup(filename);
> ```
> ```c
> 83    if (!tmp)
> 84    {
> 85      tmp = (DLLchain) malloc(sizeof(tDLLchain));
> 86      if (!tmp)
> 87            goto nomem;
> 88      tmp->name = strdup(filename);
> 89      tmp->next = dlload;
> 90      set_chain = 1;
> 91    }
> 92
> ```
> 函数声明如下
>
> ```c
> char *strdup(const char *str)
> ```
> 该函数?在复制字符串,
> 如下函数把字符串赋值给了tmp空间里面的name参数,
>
> ```c
> 88      tmp->name = strdup(filename);
> ```
> 下面是dlopen代码的最终结束段。
>
> ```c
> 105            return tmp->id;
> ```
> ```c
> 93    switch (rc = DosLoadModule((PSZ)&err, sizeof(err), filename, &hm))
> 94    {
> 95      case NO_ERROR:
> 96            tmp->handle = hm;
> 97            if (set_chain)
> 98            {
> 99                do
> 100                  last_id++;
> 101                while ((last_id == 0) || (find_id(last_id)));
> 102                tmp->id = last_id;
> 103                dlload = tmp;
> 104            }
> 105            return tmp->id;
> ```
> 此函数返回了句柄
>
> ```c
>DosLoadModule((PSZ)&err, sizeof(err), filename, &hm)
> ```
> 到此分析完毕,
> 有了句柄就能调用里面的函数了。

学编程的闹钟 发表于 2025-3-29 19:57

谢谢分享
页: [1]
查看完整版本: 安卓9.0loadLibrary源码分析