最近学习exit hook,网上师傅分析exit hook时都一带而过,没有具体分析细节,所以这里通过看源码给大家详细分析一下几种exit hook:
- __libc_atexit
- _rtld_global
- _dl_rtld_lock_recursive
- _dl_rtld_unlock_recursive
我用的是2.31的libc源码,本文涉及源码部分较多,而且只截取了关键部分,光阅读可能比较难以接受,建议大家按这个思路边调试边看一下加深理解。
__libc_atexit
调用流程:
exit->__run_exit_handlers->__libc_atexit
对exit下断点,看到调用了__run_exit_handlers
函数:
136 void
137 exit (int status)
► 138 {
139 __run_exit_handlers (status, &__exit_funcs, true, true);
140 }
__libc_atexit
是__run_exit_handlers
结束时调用到的:
源码编写者用了很多宏,光看这个是看不清的,我们一步步看有关的宏来分析。
首先,看exit.c
源码,查看__libc_atexit
相关代码,首先开头处有一处DEFINE_HOOK
宏:
DEFINE_HOOK (__libc_atexit, (void))
然后就是后面调用RUN_HOOK
宏了:
if (run_list_atexit)
RUN_HOOK (__libc_atexit, ());
_exit (status);
}
DEFINE_HOOK分析
顾名思义,DEFINE_HOOK
应该是跟声明定义有关,我们先看这个。
DEFINE_HOOK
和RUN_HOOK
两处宏都定义在set-hooks.h
头文件中,所以我们转到该文件中查看。
DEFINE_HOOK
定义:
/* Define a hook variable called NAME. Functions put on this hook take
arguments described by PROTO. Use `text_set_element (NAME, FUNCTION)'
from include/libc-symbols.h to add a function to the hook. */
# define DEFINE_HOOK(NAME, PROTO) \
typedef void __##NAME##_hook_function_t PROTO; \
symbol_set_define (NAME)
定义了一个类型,然后symbol_set_define
又是一处宏,在libc-symbols.h
中定义:
/* Define SET as a symbol set. This may be required (it is in a.out) to
be able to use the set's contents. */
#define symbol_set_define(set) symbol_set_declare(set)
/* Declare SET for use in this module, if defined in another module.
In a shared library, this is always local to that shared object.
For static linking, the set might be wholly absent and so we use
weak references. */
#define symbol_set_declare(set) \
extern char const __start_##set[] __symbol_set_attribute; \
extern char const __stop_##set[] __symbol_set_attribute;
__symbol_set_attribute
又是一处宏,不过不用再继续看下去了,很明显,这里其实就是声明了两个函数指针数组。
总结一下,DEFINE_HOOK
的作用就是定义一种函数指针类型,然后声明了两个函数指针数组。
RUN_HOOK分析
RUN_HOOK
估计是跟调用hook有关的,看宏定义:
/* Run all the functions hooked on the set called NAME.
Each function is called like this: `function ARGS'. */
# define RUN_HOOK(NAME, ARGS) \
do { \
void *const *ptr; \
for (ptr = (void *const *) symbol_set_first_element (NAME); \
! symbol_set_end_p (NAME, ptr); ++ptr) \
(*(__##NAME##_hook_function_t *) *ptr) ARGS; \
} while (0)
看到symbol_set_first_element
和symbol_set_end_p
也是宏,同样定义在libc-symbols.h
中:
/* Return a pointer (void *const *) to the first element of SET. */
#define symbol_set_first_element(set) ((void *const *) (&__start_##set))
/* Return true iff PTR (a void *const *) has been incremented
past the last element in SET. */
#define symbol_set_end_p(set, ptr) ((ptr) >= (void *const *) &__stop_##set)
很明显了,RUN_HOOK
的作用就是遍历上面DEFINE_HOOK
定义的函数指针数组。
利用
我们把上面的宏用实际参数带入,得到实际是从__start___libc_atexit
开始遍历到__stop___libc_atexit
。
gdb查看该位置,发现__start___libc_atexit
实际上是__elf_set___libc_atexit_element__IO_cleanup__
,该地址可写,指向_IO_cleanup
。
因此,我们可以修改该地址指向one_gadget或进行其他利用。
_rtld_global
这个结构体相关的exit hook,我找到一篇写得很清楚的文章,就不重新分析了,在此贴上链接:浅谈exit函数的利用方式之一——exit hook