이것은 **프로그램이 종료되기 전에 호출되는 함수**를 포함하는 구조체입니다. 이는 **`.dtors`**와 유사합니다. 이는 **주소로 점프하여 쉘코드를 호출**하거나 **취약점을 두 번째로 이용하기 위해 다시 `main`으로 돌아가야 하는 경우**에 흥미로울 수 있습니다.
**`.fini_array`**를 남용하여 무한 루프를 얻기 위해 [**여기에서 수행된 작업을 확인할 수 있습니다**](https://guyinatuxedo.github.io/17-stack\_pivot/insomnihack18\_onewrite/index.html)**:** **`.fini_array`**에 적어도 2개의 항목이 있는 경우 다음을 수행할 수 있습니다:
* 첫 번째 쓰기를 사용하여 **취약한 임의 쓰기 함수를 호출**합니다.
* 그런 다음, **`__libc_csu_fini`**에 의해 저장된 스택의 반환 주소를 계산하고 **`__libc_csu_fini`**의 주소를 거기에 넣습니다.
* 이렇게 하면 **`__libc_csu_fini`**가 자신을 다시 호출하여 **`.fini_array`** 함수를 다시 실행하게 만들어 취약한 WWW 함수를 2번 호출하게 됩니다: 하나는 **임의 쓰기**를 위해이고 다른 하나는 다시 **`__libc_csu_fini`의 반환 주소**를 덮어쓰기 위해 스택에 자신을 다시 호출합니다.
[**이 게시물**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link\_map-structure)에서 설명한대로, 프로그램이 `return` 또는 `exit()`를 사용하여 종료되면 `__run_exit_handlers()`가 실행되어 등록된 소멸자를 호출합니다.
Elf64_Xword d_val; // address of function that will be called, we put our onegadget here
Elf64_Addr d_ptr; // offset from l->l_addr of our structure
}
```
`map -> l_addr + fini_array -> d_un.d_ptr`을 사용하여 **호출할 함수 배열의 위치를 계산하는 방법**에 주목하세요.
**여러 가지 옵션이** 있습니다:
*`map->l_addr`의 값을 덮어쓰기하여 임의의 코드를 실행하는 **가짜 `fini_array`**를 가리키도록 만듭니다.
* 메모리 상에서 거의 연속적인 `l_info[DT_FINI_ARRAY]` 및 `l_info[DT_FINI_ARRAYSZ]` 항목을 덮어쓰기하여 다시 **`array`가 공격자가 제어하는 메모리 영역을 가리키도록 하는** 가짜 `Elf64_Dyn` 구조체를 가리키게 합니다. 
* [**이 writeup**](https://github.com/nobodyisnobody/write-ups/tree/main/DanteCTF.2023/pwn/Sentence.To.Hell)에서는 `.bss`에 있는 제어된 메모리 주소를 포함하는 `l_info[DT_FINI_ARRAY]`를 덮어쓰고 가짜 `fini_array`를 포함하는 가짜 배열을 만듭니다. 이 가짜 배열은 먼저 실행될 [**원 가젯**](../rop-return-oriented-programing/ret2lib/one-gadget.md) **주소**를 포함하고, 그런 다음 이 **가짜 배열**의 주소와 `map->l_addr`의 **값 사이의 차이**를 포함하여 `*array`가 가짜 배열을 가리키도록 합니다.
* 이 기술의 주요 게시물 및 [**이 writeup**](https://activities.tjhsst.edu/csc/writeups/angstromctf-2021-wallstreet)에 따르면 ld.so는 ld.so에서 이진 `link_map`을 가리키는 스택에 포인터를 남깁니다. 임의의 쓰기를 사용하여 덮어쓰고 공격자가 제어하는 가짜 `fini_array`를 가리키도록 만들고, 예를 들어 [**원 가젯**](../rop-return-oriented-programing/ret2lib/one-gadget.md)의 주소를 포함할 수 있습니다.
이 경우 `map->l_info[DT_FINI]` 값이 조작된 `ElfW(Dyn)` 구조체를 가리키도록 덮어쓸 수 있습니다. [**여기에서 자세한 정보를 확인하세요**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link\_map-structure).
[**여기에서 설명된 것**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor\_list-overwrite)와 같이, 프로그램이 `return` 또는 `exit()`를 통해 종료되면 **`__run_exit_handlers()`**가 실행되어 등록된 소멸자 함수를 호출합니다.
`_run_exit_handlers()`에서의 코드:
```c
/* Call all functions registered with `atexit' and `on_exit',
in the reverse of the order in which they were registered
perform stdio cleanup, and terminate program execution with STATUS. */
/* Call the destructors. This is called either when a thread returns from the
initial function or when the process exits via the exit function. */
void
__call_tls_dtors (void)
{
while (tls_dtor_list) // parse the dtor_list chained structures
{
struct dtor_list *cur = tls_dtor_list; // cur point to tls-storage dtor_list
dtor_func func = cur->func;
PTR_DEMANGLE (func); // demangle the function ptr
tls_dtor_list = tls_dtor_list->next; // next dtor_list structure
func (cur->obj);
[...]
}
}
```
모든 등록된 함수에 대해 **`tls_dtor_list`**에서 포인터를 **`cur->func`**에서 demangle하고 인자 **`cur->obj`**와 함께 호출합니다.
이 [**GEF의 fork**](https://github.com/bata24/gef)에서 **`tls`** 함수를 사용하면 실제로 **`dtor_list`**가 **스택 캐너리**와 **PTR\_MANGLE 쿠키**에 매우 **가깝다는 것**을 확인할 수 있습니다. 따라서 이를 **오버플로우**하여 **쿠키**와 **스택 캐너리**를 **덮어쓸 수** 있습니다.\
PTR\_MANGLE 쿠키를 덮어쓰면 0x00으로 설정하여 **`PTR_DEMANLE` 함수를 우회**할 수 있습니다. 이는 실제 주소를 얻기 위해 사용된 **`xor`**가 구성된 주소일 뿐이라는 것을 의미합니다. 그런 다음 **`dtor_list`**에 쓰면 함수 **주소**와 **인자**로 **여러 함수를 연결**할 수 있습니다.
이 기술은 [**여기에서 설명**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor\_list-overwrite)되어 있으며 다시 프로그램이 `return` 또는 `exit()`를 호출하여 종료되면 **`__run_exit_handlers()`**가 호출됩니다.
이 함수의 더 많은 코드를 확인해 봅시다:
```c
while (true)
{
struct exit_function_list *cur;
restart:
cur = *listp;
if (cur == NULL)
{
/* Exit processing complete. We will not allow any more
atexit/on_exit registrations. */
__exit_funcs_done = true;
break;
}
while (cur->idx > 0)
{
struct exit_function *const f = &cur->fns[--cur->idx];