[Dreamhack] Background: _rtld_global

Sisyphus·2022년 7월 29일
0

Dreamhack - System Hacking

목록 보기
42/49

들어가며

_rtld_global 실습 예제

// Name: rtld.c
// Compile: gcc -o rtld rtld.c

int main() {
  return 0;
}


_rtld_global

__GI_exit

gef➤  disas main
Dump of assembler code for function main:
   0x00005555554005fa <+0>:     push   rbp
   0x00005555554005fb <+1>:     mov    rbp,rsp
=> 0x00005555554005fe <+4>:     mov    eax,0x0
   0x0000555555400603 <+9>:     pop    rbp
   0x0000555555400604 <+10>:    ret
End of assembler dump.
gef➤  b * main+10
Breakpoint 1 at 0x555555400604
gef➤  r
gef➤  si
 → 0x7ffff7a03c87 <__libc_start_main+231> mov    edi, eax
   0x7ffff7a03c89 <__libc_start_main+233> call   0x7ffff7a25110 <__GI_exit>

main 함수 ret에 breakpoint를 걸고 step into를 통해 다음 코드를 살펴보면 libc_start_main+231의 코드가 실행되고, GI_exit 함수가 호출됩니다.

step into로 __GI_exit 함수 내부를 봐보면

 → 0x7ffff7a03c89 <__libc_start_main+233> call   0x7ffff7a25110 <__GI_exit>
   ↳  0x7ffff7a25110 <exit+0>         lea    rsi, [rip+0x3a8601]        # 0x7ffff7dcd718 <__exit_funcs>
      0x7ffff7a25117 <exit+7>         sub    rsp, 0x8
      0x7ffff7a2511b <exit+11>        mov    ecx, 0x1
      0x7ffff7a25120 <exit+16>        mov    edx, 0x1
      0x7ffff7a25125 <exit+21>        call   0x7ffff7a24ec0 <__run_exit_handlers>

또 다른 __run_exit_handlers 함수가 등장합니다.


__run_exit_handlers

exit_function 구조체의 멤버 변수에 따른 함수 포인터를 호출합니다. 리턴 명령어를 실행해 프로그램을 종료한다면 _dl_fini 함수를 호출합니다.

void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
		     bool run_list_atexit, bool run_dtors)
{
	  const struct exit_function *const f = &cur->fns[--cur->idx];
	  switch (f->flavor)
	    {
	      void (*atfct) (void);
	      void (*onfct) (int status, void *arg);
	      void (*cxafct) (void *arg, int status);
	    case ef_free:
	    case ef_us:
	      break;
	    case ef_on:
	      onfct = f->func.on.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (onfct);
#endif
	      onfct (status, f->func.on.arg);
	      break;
	    case ef_at:
	      atfct = f->func.at;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (atfct);
#endif
	      atfct ();
	      break;
	    case ef_cxa:
	      cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (cxafct);
#endif
	      cxafct (f->func.cxa.arg, status);
	      break;
	    }
	}
struct exit_function
{
/* `flavour' should be of type of the `enum' above but since we need
   this element in an atomic operation we have to use `long int'.  */
long int flavor;
union
  {
void (*at) (void);
struct
  {
    void (*fn) (int status, void *arg);
    void *arg;
  } on;
struct
{
    void (*fn) (void *arg, int status);
    void *arg;
    void *dso_handle;
  } cxa;
  } func;
};

_dl_fini

_dl_load_lock을 인자로 __rtld_lock_lock_recursive 함수를 호출합니다. 매크로를 보면 해당 함수는 dl_rtld_lock_recursive라는 함수 포인터입니다.

해당 함수 포인터는 __rtld_global 구조체의 멤버 변수 입니다.

# define __rtld_lock_lock_recursive(NAME) \
  GL(dl_rtld_lock_recursive) (&(NAME).mutex)
  
void
_dl_fini (void)
{
#ifdef SHARED
  int do_audit = 0;
 again:
#endif
  for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
    {
      /* Protect against concurrent loads and unloads.  */
      __rtld_lock_lock_recursive (GL(dl_load_lock));

_rtld_global

_rtld_global 구조체 내 _dl_rtld_lock_recursive 함수 포인터에는 rtld_lock_default_lock_recursive 함수 주소를 저장하고 있습니다. 구조체의 함수 포인터가 저장된 영역은 읽기 및 쓰기 권한이 존재하기 때문에 덮어쓰는 것이 가능합니다.

gdb-peda$ p _rtld_global
_dl_load_lock = {
    mutex = {
      __data = {
        __lock = 0x0, 
        __count = 0x0, 
        __owner = 0x0, 
        __nusers = 0x0, 
        __kind = 0x1, 
        __spins = 0x0, 
        __elision = 0x0, 
        __list = {
          __prev = 0x0, 
          __next = 0x0
        }
      }, 
      __size = '\000' <repeats 16 times>, "\001", '\000' <repeats 22 times>, 
      __align = 0x0
    }
  },
  _dl_rtld_lock_recursive = 0x7ffff7dd60e0 <rtld_lock_default_lock_recursive>, 
  ...
}
gdb-peda$ p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7ffff7ffdf60 <_rtld_global+3840>
gdb-peda$ vmmap 0x7ffff7ffdf60
Start              End                Perm	Name
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/lib/x86_64-linux-gnu/ld-2.27.so

_rtld_global 초기화

프로세스를 로드할 대 호출되는 dl_main 코드 일부를 봐보면,
_rtld_global 구조체의 dl_rtld_lock_recursive 함수 포인터가 초기화 되는 것을 확인할 수 있습니다.

static void
dl_main (const ElfW(Phdr) *phdr,
	 ElfW(Word) phnum,
	 ElfW(Addr) *user_entry,
	 ElfW(auxv_t) *auxv)
{
  GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
#if defined SHARED && defined _LIBC_REENTRANT \
    && defined __rtld_lock_default_lock_recursive
  GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive;
  GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive;

0개의 댓글