서론
리눅스 프로그램을 실행하면 프로그램에 명시된 코드의 결과값이 우리에게 출력된다. 프로그램이 실행되어 프로세스로 등록될 때에는 프로그램에 명시된 코드 뿐만이 아니라 프로그램에서 쓰이는 변수를 관리하기 위한 영역을 할당하는 등의 다양한 코드가 로더에 의해 실행된다.
프로세스가 등록되거나 종료될 때 쓰이는 변수와 영역들을 알고 있다면 이를 이용한 새로운 공격 기법을 개발할 수 있다. 이번에는 라이브러리의 코드를 분석하면서 어떤 방식으로 프로세스를 종료하는지 알아보도록 하자.
아래 코드를 컴파일한다.
// Name: rtld.c
// Compile: gcc -o rtld rtld.c
int main() {
return 0;
}
_rtld_global
_GI_exit
앞서 컴파일 한 예제 코드는 별다른 코드를 실행하지 않고 프로그램을 종료한다. 프로그램을 종료할 때에는 우리가 모르는 많은 코드들이 내부적으로 실행되는데, 이를 디버깅을 통해 살펴본다.
main 함수 내 리턴하는 명령어에 브레이크포인트를 설정하고, step into를 통해 다음 코드를 확인해본다.

결과를 살펴보면, main 함수 내에서 리턴 명령어를 실행하면 스택 최상단에 있는 __libc_start_main+231의 코드가 실행되고, 내부에서 __GI_exit 함수를 호출하는 것을 볼 수 있다.
step into 명령어를 계속 실행해서 살펴본다.

위의 사진은 __GI_exit 함수 내부의 모습으로, 또 다른 __run_exit_handlers 함수가 등장한다.
__run_exit_handlers
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;
}
}
위 코드는 __run_exit_handlers 함수의 코드로, exit_function 구조체의 멤버 변수에 따른 함수 포인터를 호출한다. 해당 구조체의 모습은 아래 코드와 같으며, 예제와 같이 리턴 명령어를 실행해 프로그램을 종료한다면
_dL_fini 함수를 호출한다.
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_fini 함수 코드의 일부이다. 코드를 살펴보면, _dl_load_lock을 인자로 __rtld_lock_lock_recursive 함수를 호출하는 것을 볼 수 있다. 매크로를 확인해보면, 해당 함수는 dl_rtld_lock_recursive라는 함수 포인터임을 알 수 있다.
해당 함수 포인터는 _rtld_global 구조체의 멤버 변수이다. 해당 구조체는 매우 방대하기 때문에 함수 포인터와 전달되는 인자인 dl_load_lock만을 살펴본다.
# 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
아래 코드는 gdb에서 _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;'시스템해킹 > 개념' 카테고리의 다른 글
| Double Free bug와 Fastbin dup 복습 (0) | 2025.06.25 |
|---|---|
| [Dreamhack-system] Background : _IO_FILE (1) | 2024.12.20 |
| [Dreamhack-system] SigReturn-Oriented Programming (3) | 2024.12.18 |
| [Dreamhack-system] Background: Master Canary (2) | 2024.12.16 |
| [Dreamhack-system] Background: SECCOMP (4) | 2024.12.15 |