> For the complete documentation index, see [llms.txt](https://hacktricks.xsx.tw/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacktricks.xsx.tw/binary-exploitation/write-what-where-2-exec/www2exec-.dtors-and-.fini_array.md).

# WWW2Exec - .dtors & .fini\_array

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>！</strong></summary>

其他支持HackTricks的方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

## .dtors

{% hint style="danger" %}
现在很**奇怪找到一个带有.dtors部分的二进制文件！**
{% endhint %}

析构函数是在程序结束之前（在`main`函数返回后）执行的函数。\
这些函数的地址存储在二进制文件的\*\*`.dtors`**部分中，因此，如果您设法**写入**一个**shellcode**的**地址**到**`__DTOR_END__`**，那么它将在程序结束之前被**执行\*\*。

使用以下命令获取此部分的地址：

```bash
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
```

通常你会在值`ffffffff`和`00000000`之间找到**DTOR**标记。所以如果你只看到这些值，意味着**没有任何函数注册**。因此，**覆盖**`00000000`的**地址**为**shellcode**的地址以执行它。

{% hint style="warning" %}
当然，你首先需要找到一个**存储shellcode的位置**，以便稍后调用它。
{% endhint %}

## **.fini\_array**

本质上，这是一个包含在程序完成之前将被调用的**函数的结构**，就像\*\*`.dtors`**一样。如果你可以通过**跳转到一个地址调用你的shellcode\*\*，或者在需要**返回到`main`以第二次利用漏洞**的情况下，这就变得很有趣。

```bash
objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934
```

请注意，当从 **`.fini_array`** 中的一个函数执行时，它会移动到下一个函数，因此不会被多次执行（防止无限循环），但它也只会给你提供一个在这里放置的 **函数执行**。

请注意，`.fini_array` 中的条目按**相反**顺序调用，因此您可能希望从最后一个开始编写。

#### 无限循环

为了滥用 **`.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`** 的**返回地址**，以再次调用自身。

{% hint style="danger" %}
请注意，使用[**Full RELRO**](/binary-exploitation/common-binary-protections-and-bypasses/relro.md)**，** **`.fini_array`** 部分被设置为**只读**。
{% endhint %}

## link\_map

如[**此帖子**](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()`，该函数将调用已注册的析构函数。

{% hint style="danger" %}
如果程序通过 **`_exit()`** 函数退出，它将调用 **`exit` 系统调用**，退出处理程序将不会被执行。因此，要确认 `__run_exit_handlers()` 是否被执行，您可以在其上设置断点。
{% endhint %}

重要代码为（[源代码](https://elixir.bootlin.com/glibc/glibc-2.32/source/elf/dl-fini.c#L131)）:

```c
ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY];
if (fini_array != NULL)
{
ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr + fini_array->d_un.d_ptr);
size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)));

while (sz-- > 0)
((fini_t) array[sz]) ();
}
[...]




// This is the d_un structure
ptype l->l_info[DT_FINI_ARRAY]->d_un
type = union {
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]`条目（在内存中更或多少是连续的），使它们指向一个伪造的`Elf64_Dyn`结构，再次使\*\*`array`指向攻击者控制的内存区域\*\*。
* [**这篇文章**](https://github.com/nobodyisnobody/write-ups/tree/main/DanteCTF.2023/pwn/Sentence.To.Hell)用受控内存中`.bss`中的地址覆盖了`l_info[DT_FINI_ARRAY]`，其中包含一个伪造的`fini_array`。这个伪造数组首先包含一个[**one gadget**](/binary-exploitation/rop-return-oriented-programing/ret2lib/one-gadget.md)的地址，然后是这个**伪造数组**地址与`map->l_addr`值之间的**差异**，以便`*array`指向伪造数组。
* 根据这种技术的主要帖子和[**这篇文章**](https://activities.tjhsst.edu/csc/writeups/angstromctf-2021-wallstreet)，ld.so在栈上留下一个指向ld.so中的二进制`link_map`的指针。通过任意写入，可以覆盖它并使其指向由攻击者控制的伪造`fini_array`，其中包含一个[**one gadget**](/binary-exploitation/rop-return-oriented-programing/ret2lib/one-gadget.md)的地址，例如。

在前面的代码之后，您可以找到另一个有趣的部分，其中包含以下代码：

```c
/* Next try the old-style destructor.  */
ElfW(Dyn) *fini = map->l_info[DT_FINI];
if (fini != NULL)
DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr));
}
```

在这种情况下，可以覆盖`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)。

## 在\*\*`__run_exit_handlers`\*\*中覆盖TLS存储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()`\*\*，该函数将调用任何已注册的析构函数。

来自`_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.  */
void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit, bool run_dtors)
{
/* First, call the TLS destructors.  */
#ifndef SHARED
if (&__call_tls_dtors != NULL)
#endif
if (run_dtors)
__call_tls_dtors ();
```

**`__call_tls_dtors()`** 函数的代码：

```c
typedef void (*dtor_func) (void *);
struct dtor_list //struct added
{
dtor_func func;
void *obj;
struct link_map *map;
struct dtor_list *next;
};

[...]
/* 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`**中解码指针并使用参数**`cur->obj`\*\*调用它。

使用来自[**GEF的这个分支**](https://github.com/bata24/gef)中的\*\*`tls`**函数，可以看到**`dtor_list`**实际上非常**接近**栈保护和PTR\_MANGLE cookie\*\*。因此，通过对其进行溢出，可以\*\*覆盖**cookie**和**栈保护\*\*。\
覆盖PTR\_MANGLE cookie，将有可能**绕过`PTR_DEMANLE`函数**，因为将其设置为0x00，将意味着用于获取真实地址的\*\*`xor`**仅是配置的地址。然后，在**`dtor_list`**上写入，可以使用函数**地址**及其**参数**来**链接多个函数\*\*。

最后请注意，存储的指针不仅会与cookie进行异或运算，还会旋转17位：

```armasm
0x00007fc390444dd4 <+36>:	mov    rax,QWORD PTR [rbx]      --> mangled ptr
0x00007fc390444dd7 <+39>:	ror    rax,0x11		        --> rotate of 17 bits
0x00007fc390444ddb <+43>:	xor    rax,QWORD PTR fs:0x30	--> xor with PTR_MANGLE
```

在添加新地址之前，您需要考虑这一点。

在[**原始帖子**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite)中找一个例子。

## **`__run_exit_handlers`** 中的其他混淆指针

这种技术在[**这里解释**](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];
const uint64_t new_exitfn_called = __new_exitfn_called;

switch (f->flavor)
{
void (*atfct) (void);
void (*onfct) (int status, void *arg);
void (*cxafct) (void *arg, int status);
void *arg;

case ef_free:
case ef_us:
break;
case ef_on:
onfct = f->func.on.fn;
arg = f->func.on.arg;
PTR_DEMANGLE (onfct);

/* Unlock the list while we call a foreign function.  */
__libc_lock_unlock (__exit_funcs_lock);
onfct (status, arg);
__libc_lock_lock (__exit_funcs_lock);
break;
case ef_at:
atfct = f->func.at;
PTR_DEMANGLE (atfct);

/* Unlock the list while we call a foreign function.  */
__libc_lock_unlock (__exit_funcs_lock);
atfct ();
__libc_lock_lock (__exit_funcs_lock);
break;
case ef_cxa:
/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
we must mark this function as ef_free.  */
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
arg = f->func.cxa.arg;
PTR_DEMANGLE (cxafct);

/* Unlock the list while we call a foreign function.  */
__libc_lock_unlock (__exit_funcs_lock);
cxafct (arg, status);
__libc_lock_lock (__exit_funcs_lock);
break;
}

if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
/* The last exit function, or another thread, has registered
more exit functions.  Start the loop over.  */
goto restart;
}

*listp = cur->next;
if (*listp != NULL)
/* Don't free the last element in the chain, this is the statically
allocate element.  */
free (cur);
}

__libc_lock_unlock (__exit_funcs_lock);
```

变量`f`指向\*\*`initial`**结构，根据`f->flavor`的值不同，将调用不同的函数。**\
**根据数值不同，要调用的函数地址会在不同的位置，但它们始终会被**解码\*\*。

此外，在选项\*\*`ef_on`**和**`ef_cxa`**中，还可以控制一个**参数\*\*。

可以在使用GEF运行\*\*`gef> p initial`**的调试会话中检查**`initial`结构\*\*。

要利用这一点，您需要**泄漏或擦除`PTR_MANGLE`cookie**，然后用`system('/bin/sh')`覆盖`initial`中的`cxa`条目。\
您可以在[**有关该技术的原始博客文章**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#6---code-execution-via-other-mangled-pointers-in-initial-structure)中找到一个示例。

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们独家的[NFTs](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**上关注**我们。
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>
