首页
社区
课程
招聘
[原创] 安卓9.0loadLibrary源码分析
发表于: 2025-3-28 11:53 1691

[原创] 安卓9.0loadLibrary源码分析

2025-3-28 11:53
1691

安卓源代码来自如下网站
fc1K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4F1k6s2u0G2K9h3c8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3
在这里插入图片描述
我就选最新的9.0系统分析了Pie - 9.0.0_r3
这里搜索LoadLibrary,右边select all 勾选上
在这里插入图片描述
然后点击Search,可以看到相关当前方法的调用。
在这里插入图片描述
在这之前先说一下LoadLibrary方法的用法
LoadLibrary来自System对象,所以调用时,
需要前面加上System,由于是直接调用的,
因此此对象不需要实例化,直接System.loadlibrary,
loadlibrary方法会专门调用apk包中lib文件夹下的so文件,
下面拿一个安卓apk做示范,
在这里插入图片描述
这里我用的是360压缩直接打开的,
apk文件结构如下
在这里插入图片描述
继续回到安卓系统源码上,搜索后,一个一个看,
看到类似的函数声明
此函数返回值为万能指针,能存放所有数据类型的空间,
第一个方法数据类型为字符型,是调用LoadLibrary函数的
so文件路径,在调用loadLibrary函数时安卓端省略了路径,
省略了lib前缀,省略了.so后缀,例如libLanAn.so
只需要输入System.loadLibrary(“LanAn”),即可,

1
2
3
4
5
6
7
8
9
10
11
193  void* (*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 [IN] the handle returned from loadLibrary
200  //   shorty [IN] short descriptor of native method
201  //   len [IN] length of shorty
202  // Returns:
203  //   address of trampoline if successful, otherwise NULL

源码跟踪示例
在这里插入图片描述

在这里插入图片描述
点进去可以看到如下函数声明,这里只有声明
因此还需继续搜索,

1
void* (*loadLibrary)(const char* libpath, int flag);

可以试试搜索 void* loadLibrary这个函数
如下是搜索这个函数找到的结果
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
30void *loadLibrary(const char *(&names)[n], const char *mustContainSymbol = nullptr)
31{
32  for(int i = 0; i < n; i++)
33  {
34      void *library = getLibraryHandle(names[i]);
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[i]);
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文件

1
void *library = getLibraryHandle(names[i]);

如下是此函数getLibraryHandle的声明

1
24void *getLibraryHandle(const char *path);

下面是关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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

继续搜寻,发现该函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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文件,并且返回了文件句柄

1
return dlopen(path, RTLD_NOW | RTLD_LOCAL);

继续跟踪dlopen函数

在这里插入图片描述
如下是dlopen函数的实现

1
void *dlopen(char *filename, int flags)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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[256];
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函数的参数,
如下代码在对比文件名字

1
2
80        if (strnicmp(tmp->name, filename, 999) == 0)
81            break;

继续跟踪
这里关键代码为

1
88        tmp->name = strdup(filename);
1
2
3
4
5
6
7
8
9
10
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

函数声明如下

1
char *strdup(const char *str)

该函数‌在复制字符串,
如下函数把字符串赋值给了tmp空间里面的name参数,

1
88        tmp->name = strdup(filename);

下面是dlopen代码的最终结束段。

1
105            return tmp->id;
1
2
3
4
5
6
7
8
9
10
11
12
13
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;

此函数返回了句柄

1
DosLoadModule((PSZ)&err, sizeof(err), filename, &hm)

到此分析完毕,
有了句柄就能调用里面的函数了。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
离谱
2025-3-28 12:53
0
游客
登录 | 注册 方可回帖
返回