injectso dl_open失败分析
时间:2007-04-17 来源:loughsky
injectso技术应该是比较成熟的技术,但看其相关的文档却大多是前几年的。
我按照injectso教程里面讲述的方式,实验了一下,却报出了下面的错误。
error while loading shared libraries: dlopen: invalid caller 按原来的文档,_dl_open的参数caller,是无关紧要的,设置为0。实际上看来并非如此。 查看了glibc的代码,也没有发现能够报invalid caller的地方,最后,找到了一个patch。
http://www.filewatcher.com/p/compat-glibc-2.3.2-95.30.src.rpm.14008569/glibc-execstack-fix3.patch.html 在这里
static void
dl_open_worker (void *a)
{
@@ -166,11 +232,17 @@ dl_open_worker (void *a)
bool any_tls;
#endif
+#ifdef SHARED
+ /* Check whether _dl_open() has been called from a valid DSO. */
+ if (check_libc_caller (args->caller_dl_open) != 0)
+ _dl_signal_error (0, "dlopen", NULL, N_("invalid caller")); 那么我来看下args是如何定义的?
@@ -66,7 +67,10 @@ struct dl_open_args
{
const char *file;
int mode;
- const void *caller;
+ /* This is the caller of the dlopen() function. */
+ const void *caller_dlopen;
+ /* This is the caller if _dl_open(). */
+ const void *caller_dl_open;
struct link_map *map;
}; 我们再来看下check_libc_caller就会明白了。
static int
+internal_function
+check_libc_caller (const void *caller)
+{
+ static const char expected1[] = LIBC_SO;
+ static const char expected2[] = LIBDL_SO;
+
+ /* If we already know the address ranges, just test. */
+ static const void *expected1_from;
+ static const void *expected1_to;
+ static const void *expected2_from;
+ static const void *expected2_to;
+
+ if (expected1_from == NULL)
+ {
+ /* The only other DSO which is allowed to call these functions is
+ libdl. Find the address range containing the caller. */
+ struct link_map *l;
+
+ for (l = GL(dl_loaded); l != NULL; l = l->l_next)
+ if (strcmp (expected1, l->l_name) == 0)
+ {
+ is_1:
+ expected1_from = (const void *) l->l_map_start;
+ expected1_to = (const void *) l->l_map_end;
+ }
+ else if (strcmp (expected1, l->l_name) == 0)
+ {
+ is_2:
+ expected2_from = (const void *) l->l_map_start;
+ expected2_to = (const void *) l->l_map_end;
+ }
+ else
+ {
+ struct libname_list *runp = l->l_libname;
+
+ while (runp != NULL)
+ {
+ if (strcmp (expected1, runp->name) == 0)
+ goto is_1;
+ else if (strcmp (expected2, runp->name) == 0)
+ goto is_2;
+
+ runp = runp->next;
+ }
+ }
+
+ assert (expected1_from != NULL);
+ }
+
+ /* When there would be more than two expected caller we could use an
+ array for the values but for now this is cheaper. */
+ if ((caller >= expected1_from && caller < expected1_to)
+ || (caller >= expected2_from && caller < expected2_to))
+ return 0;
+
+ return 1;
+} 也就是说,它会来判断caller指针所在的范围,他应该在LIBC_SO LIBDL_SO映射的库文件中,
#define LIBC_SO "libc.a"
#define LIBDL_SO "libdl.a"
也就是说,调用dl_open的函数,应该在这两个库中。 因此,我怀疑,glibc为了防止injectso,而在glibc的代码中打了一些patch,来防止这个问题。
而我们正常的在程序中调用函数dl_open时,我们在编译的时候增加了一个选项 -ldl,这就是指要使用libdl.a 那么injectso的这条路是不是就走不通了吗? 我考虑是否可以自己伪造一个linkmap表,来欺骗它。但这样的话,对于那些真的使用libdl的,会造成问题。最好能在程序load时,不管用不用,都自动把libdl库load进来。
error while loading shared libraries: dlopen: invalid caller 按原来的文档,_dl_open的参数caller,是无关紧要的,设置为0。实际上看来并非如此。 查看了glibc的代码,也没有发现能够报invalid caller的地方,最后,找到了一个patch。
http://www.filewatcher.com/p/compat-glibc-2.3.2-95.30.src.rpm.14008569/glibc-execstack-fix3.patch.html 在这里
static void
dl_open_worker (void *a)
{
@@ -166,11 +232,17 @@ dl_open_worker (void *a)
bool any_tls;
#endif
+#ifdef SHARED
+ /* Check whether _dl_open() has been called from a valid DSO. */
+ if (check_libc_caller (args->caller_dl_open) != 0)
+ _dl_signal_error (0, "dlopen", NULL, N_("invalid caller")); 那么我来看下args是如何定义的?
@@ -66,7 +67,10 @@ struct dl_open_args
{
const char *file;
int mode;
- const void *caller;
+ /* This is the caller of the dlopen() function. */
+ const void *caller_dlopen;
+ /* This is the caller if _dl_open(). */
+ const void *caller_dl_open;
struct link_map *map;
}; 我们再来看下check_libc_caller就会明白了。
static int
+internal_function
+check_libc_caller (const void *caller)
+{
+ static const char expected1[] = LIBC_SO;
+ static const char expected2[] = LIBDL_SO;
+
+ /* If we already know the address ranges, just test. */
+ static const void *expected1_from;
+ static const void *expected1_to;
+ static const void *expected2_from;
+ static const void *expected2_to;
+
+ if (expected1_from == NULL)
+ {
+ /* The only other DSO which is allowed to call these functions is
+ libdl. Find the address range containing the caller. */
+ struct link_map *l;
+
+ for (l = GL(dl_loaded); l != NULL; l = l->l_next)
+ if (strcmp (expected1, l->l_name) == 0)
+ {
+ is_1:
+ expected1_from = (const void *) l->l_map_start;
+ expected1_to = (const void *) l->l_map_end;
+ }
+ else if (strcmp (expected1, l->l_name) == 0)
+ {
+ is_2:
+ expected2_from = (const void *) l->l_map_start;
+ expected2_to = (const void *) l->l_map_end;
+ }
+ else
+ {
+ struct libname_list *runp = l->l_libname;
+
+ while (runp != NULL)
+ {
+ if (strcmp (expected1, runp->name) == 0)
+ goto is_1;
+ else if (strcmp (expected2, runp->name) == 0)
+ goto is_2;
+
+ runp = runp->next;
+ }
+ }
+
+ assert (expected1_from != NULL);
+ }
+
+ /* When there would be more than two expected caller we could use an
+ array for the values but for now this is cheaper. */
+ if ((caller >= expected1_from && caller < expected1_to)
+ || (caller >= expected2_from && caller < expected2_to))
+ return 0;
+
+ return 1;
+} 也就是说,它会来判断caller指针所在的范围,他应该在LIBC_SO LIBDL_SO映射的库文件中,
#define LIBC_SO "libc.a"
#define LIBDL_SO "libdl.a"
也就是说,调用dl_open的函数,应该在这两个库中。 因此,我怀疑,glibc为了防止injectso,而在glibc的代码中打了一些patch,来防止这个问题。
而我们正常的在程序中调用函数dl_open时,我们在编译的时候增加了一个选项 -ldl,这就是指要使用libdl.a 那么injectso的这条路是不是就走不通了吗? 我考虑是否可以自己伪造一个linkmap表,来欺骗它。但这样的话,对于那些真的使用libdl的,会造成问题。最好能在程序load时,不管用不用,都自动把libdl库load进来。
相关阅读 更多 +