<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>漏洞函数 on Vortex's Blog</title><link>https://3133q.github.io/tags/%E6%BC%8F%E6%B4%9E%E5%87%BD%E6%95%B0/</link><description>Recent content in 漏洞函数 on Vortex's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 19 Oct 2026 17:41:53 +0800</lastBuildDate><atom:link href="https://3133q.github.io/tags/%E6%BC%8F%E6%B4%9E%E5%87%BD%E6%95%B0/index.xml" rel="self" type="application/rss+xml"/><item><title>漏洞函数汇总</title><link>https://3133q.github.io/p/vuln-functions/</link><pubDate>Mon, 19 Oct 2026 17:41:53 +0800</pubDate><guid>https://3133q.github.io/p/vuln-functions/</guid><description>&lt;img src="https://3133q.github.io/covers/02.jpg" alt="Featured image of post 漏洞函数汇总" /&gt;&lt;h2 id="readfread"&gt;read/fread&#10;&lt;/h2&gt;&lt;h3 id="原型"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;ssize_t read(int fd, void *buf, size_t count)&lt;/p&gt;&#10;&lt;h3 id="逻辑"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;从指定的文件描述符 &lt;code&gt;fd&lt;/code&gt;（标准输入通常为 0）读取最多 &lt;code&gt;count&lt;/code&gt; 字节的数据到 &lt;code&gt;buf&lt;/code&gt; 中。它是系统调用封装，完全不关心数据内容：不补 &lt;code&gt;\x00&lt;/code&gt;，也不将 &lt;code&gt;\n&lt;/code&gt; 视作终止符。&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点"&gt;漏洞存在点：&#10;&lt;/h3&gt;&lt;ul&gt;&#10;&lt;li&gt;显式缓冲区溢出： 若 &lt;code&gt;count &amp;gt; sizeof(buf)&lt;/code&gt;，直接导致栈/堆溢出。&lt;/li&gt;&#10;&lt;li&gt;整型溢出/无符号转换（Integer Overflow/Signedness）： &lt;code&gt;count&lt;/code&gt; 常为无符号类型（&lt;code&gt;size_t&lt;/code&gt;），若外部校验代码使用有符号整型（如 &lt;code&gt;int len&lt;/code&gt;）校验且未过滤负数，传入 &lt;code&gt;-1&lt;/code&gt; 时可通过 &lt;code&gt;len &amp;lt; MAX&lt;/code&gt; 检查，但在传递给 &lt;code&gt;read&lt;/code&gt; 时被解释为超大正数 &lt;code&gt;0xFFFFFFFF&lt;/code&gt;，造成巨量溢出。&lt;/li&gt;&#10;&lt;li&gt;未截断导致信息泄露（Leak）： 因 &lt;code&gt;read&lt;/code&gt; 不会自动在末尾填充 &lt;code&gt;\x00&lt;/code&gt;，若紧接着使用 &lt;code&gt;puts&lt;/code&gt; 或 &lt;code&gt;printf(&amp;quot;%s&amp;quot;)&lt;/code&gt; 打印该缓冲区，会顺带泄露缓冲区后未初始化的内存（如 canary、栈地址或 libc 地址）。&lt;/li&gt;&#10;&lt;li&gt;Off-by-One： 如 &lt;code&gt;read(0, buf, sizeof(buf) + 1)&lt;/code&gt;，刚好覆盖相邻变量或栈帧的 saved RBP 最低字节。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="getsgetline"&gt;gets/getline&#10;&lt;/h2&gt;&lt;h3 id="原型-1"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;char *gets(char *s)&lt;/p&gt;&#10;&lt;h3 id="逻辑-1"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;从标准输入不断读取字符并存入 &lt;code&gt;s&lt;/code&gt;，直到遇到换行符 &lt;code&gt;\n&lt;/code&gt; 或 &lt;code&gt;EOF&lt;/code&gt; 为止，最后把换行符替换为 &lt;code&gt;\0&lt;/code&gt;&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-1"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;绝对无界溢出： &lt;code&gt;gets&lt;/code&gt; 完全没有长度上限限制，输入多长就写多长，是 PWN 中最经典的无条件栈溢出函数&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="fgets"&gt;fgets&#10;&lt;/h2&gt;&lt;h3 id="原型-2"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;char *fgets(char *s, int size, FILE *stream)&lt;/p&gt;&#10;&lt;h3 id="逻辑-2"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;最多读取 &lt;code&gt;size - 1&lt;/code&gt; 个字符，或读到换行符 &lt;code&gt;\n&lt;/code&gt; 为止，并在末尾&lt;strong&gt;强制追加 &lt;code&gt;\0&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-2"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;参数大小传错： 若程序员误将 &lt;code&gt;sizeof(pointer)&lt;/code&gt;（8字节）当作缓冲区大小，或者误将结构体总大小当成了单一字段的大小（如 &lt;code&gt;fgets(s-&amp;gt;name, sizeof(*s), stdin)&lt;/code&gt;）。&lt;/p&gt;&#10;&lt;p&gt;Off-by-One 边界误判： 虽然本身安全，但若搭配错误的索引逻辑（如程序员以为读了 &lt;code&gt;size&lt;/code&gt; 字节而手动在 &lt;code&gt;s[size]&lt;/code&gt; 赋值），仍会溢出。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="scanf__iso99_scanffscanf"&gt;scanf/__iso99_scanf/fscanf&#10;&lt;/h2&gt;&lt;h3 id="原型-3"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;int scanf(const char *format, &amp;hellip;)&lt;/p&gt;&#10;&lt;h3 id="逻辑-3"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;根据格式化字符串解析标准输入。遇到空白字符（空格、Tab、换行）通常作为分割符。&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-3"&gt;漏洞存在点：&#10;&lt;/h3&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;%s&lt;/code&gt; 无界溢出： 若直接使用 &lt;code&gt;scanf(&amp;quot;%s&amp;quot;, buf)&lt;/code&gt;，行为与 &lt;code&gt;gets&lt;/code&gt; 等价，读入任意长度字符直至遇到空白字符。&lt;em&gt;(安全写法应为 &lt;code&gt;%31s&lt;/code&gt;)&lt;/em&gt;。&lt;/li&gt;&#10;&lt;li&gt;类型不匹配截断： 用 &lt;code&gt;%d&lt;/code&gt; 读入整数存入 &lt;code&gt;short&lt;/code&gt; 或 &lt;code&gt;char&lt;/code&gt; 变量，多余字节直接覆盖相邻变量。&lt;/li&gt;&#10;&lt;li&gt;跳过输入利用： 当使用 &lt;code&gt;%d&lt;/code&gt; 但用户输入 &lt;code&gt;+&lt;/code&gt; 或非数字字符时，&lt;code&gt;scanf&lt;/code&gt; 会匹配失败，对应变量不会被赋值且保持原值，可用来绕过逻辑检测或保留栈上残留值。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="printfsprintfsnprintfdprintf"&gt;printf/sprintf/snprintf/dprintf&#10;&lt;/h2&gt;&lt;h3 id="原型-4"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;int printf(const char *format, &amp;hellip;)&lt;/p&gt;&#10;&lt;h3 id="逻辑-4"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;解析 &lt;code&gt;format&lt;/code&gt; 中的占位符（如 &lt;code&gt;%d&lt;/code&gt;, &lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;%p&lt;/code&gt; 等）。遇到占位符时，依次从寄存器（x86-64 依调用约定为 RSI, RDX, RCX, R8, R9）以及栈顶读取参数并格式化输出&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-4"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;直接以用户输入作为 format： 如 &lt;code&gt;printf(buf);&lt;/code&gt; 而不是 &lt;code&gt;printf(&amp;quot;%s&amp;quot;, buf);&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;任意地址读（Leak）：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;%p&lt;/code&gt; / &lt;code&gt;%x&lt;/code&gt;：泄露寄存器与栈数据（Canary、PIE基址、Libc基址）。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;%s&lt;/code&gt;：将指定栈参数视作指针，解引用读取该地址处的字符串，实现跨空间任意读。&lt;/li&gt;&#10;&lt;li&gt;Positional 语法：如 &lt;code&gt;%7$p&lt;/code&gt; 可直接定位读取第 7 个参数。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;任意地址写：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;%n&lt;/code&gt; / &lt;code&gt;%hn&lt;/code&gt; / &lt;code&gt;%hhn&lt;/code&gt;：将目前已经打印的字符总数写入对应指针指向的内存地址。配合格式化字符串构造，可以向 GOT 表、Hook 指针、返回地址等写入目标值。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;​ &lt;code&gt;snprintf&lt;/code&gt; 的逻辑截断陷阱： &lt;code&gt;snprintf(buf, size, ...)&lt;/code&gt; 返回的是完全格式化所需字符数，而非实际写入的字符数；若后续使用其返回值做偏移累加，可能导致堆/栈上的越界写。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="puts"&gt;puts&#10;&lt;/h2&gt;&lt;h3 id="原型-5"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;int puts(const char *s)&lt;/p&gt;&#10;&lt;h3 id="逻辑-5"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;从 &lt;code&gt;s&lt;/code&gt; 指向的地址开始遍历打印字符，直到遇到第一个 &lt;code&gt;\0&lt;/code&gt; 为止，并在末尾自动追加换行符 &lt;code&gt;\n&lt;/code&gt;&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-5"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;内存泄露（Leak）： 如果前序输入函数（如 &lt;code&gt;read&lt;/code&gt;）填满了缓冲区而没有保留 &lt;code&gt;\0&lt;/code&gt;，&lt;code&gt;puts&lt;/code&gt; 会接着往下读，直到内存中出现 &lt;code&gt;\0&lt;/code&gt;，从而泄露栈上的敏感数据（如 Canary、Saved RBP、返回地址 libc 等）。&lt;/p&gt;&#10;&lt;p&gt;ROP Chain 常用 Gadget： 经常被用来在 ROP 阶段构造泄露，例如 &lt;code&gt;pop rdi; ret&lt;/code&gt; 加载 GOT 表项地址后 &lt;code&gt;call puts&lt;/code&gt;。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="write"&gt;write&#10;&lt;/h2&gt;&lt;h3 id="原型-6"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;ssize_t write(int fd, const void *buf, size_t count)&lt;/p&gt;&#10;&lt;h3 id="逻辑-6"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;从 &lt;code&gt;buf&lt;/code&gt; 开始向 &lt;code&gt;fd&lt;/code&gt; 精确输出 &lt;code&gt;count&lt;/code&gt; 个字节。不依赖 &lt;code&gt;\0&lt;/code&gt; 截断&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-6"&gt;漏洞存在点：&#10;&lt;/h3&gt;&lt;ul&gt;&#10;&lt;li&gt;未初始化/越界泄露： 若 &lt;code&gt;count&lt;/code&gt; 大于有效数据长度，会忠实地把内存中紧随其后的内容原封不动吐出来，适合用来精准泄露任意长度的内存块（包括中间含有 &lt;code&gt;\0&lt;/code&gt; 的指针）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="strcpystrcat"&gt;strcpy/strcat&#10;&lt;/h2&gt;&lt;h3 id="原型-7"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;char *strcpy(char *dest, const char *src)&lt;/p&gt;&#10;&lt;h3 id="逻辑-7"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;依次复制字节直至遇到 &lt;code&gt;src&lt;/code&gt; 中的 &lt;code&gt;\0&lt;/code&gt;，连同 &lt;code&gt;\0&lt;/code&gt; 一并写入 &lt;code&gt;dest&lt;/code&gt;&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-7"&gt;漏洞存在点：&#10;&lt;/h3&gt;&lt;ul&gt;&#10;&lt;li&gt;缺乏边界检查： 若 &lt;code&gt;strlen(src) &amp;gt;= sizeof(dest)&lt;/code&gt;，发生经典栈/堆溢出。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="strncpystrncat"&gt;strncpy/strncat&#10;&lt;/h2&gt;&lt;h3 id="原型-8"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;char *strncpy(char *dest, const char *src, size_t n)&lt;/p&gt;&#10;&lt;p&gt;char *strncat(char *dest, const char *src, size_t n)&lt;/p&gt;&#10;&lt;h3 id="逻辑-8"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;strncpy&lt;/code&gt;：最多复制 &lt;code&gt;n&lt;/code&gt; 个字节。&lt;strong&gt;关键特性&lt;/strong&gt;：如果 &lt;code&gt;src&lt;/code&gt; 长度 $\ge n$，复制完 &lt;code&gt;n&lt;/code&gt; 字节后&lt;strong&gt;不会在末尾补 &lt;code&gt;\0&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;strncat&lt;/code&gt;：在 &lt;code&gt;dest&lt;/code&gt; 原有末尾追加最多 &lt;code&gt;n&lt;/code&gt; 个字符，&lt;strong&gt;并且始终会额外追加一个 &lt;code&gt;\0&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-8"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;strncpy&lt;/code&gt; 丢失 &lt;code&gt;\0&lt;/code&gt;：&lt;/strong&gt; 紧随其后的字符串操作（如 &lt;code&gt;strlen&lt;/code&gt;, &lt;code&gt;strcpy&lt;/code&gt;, &lt;code&gt;puts&lt;/code&gt;）会越界继续向后找 &lt;code&gt;\0&lt;/code&gt;，导致信息泄露或溢出。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;strncat&lt;/code&gt; Off-by-One：&lt;/strong&gt; 开发者常写错：&lt;code&gt;strncat(dest, src, sizeof(dest) - strlen(dest))&lt;/code&gt;，因为其额外追加的 &lt;code&gt;\0&lt;/code&gt; 会写到 &lt;code&gt;dest[sizeof(dest)]&lt;/code&gt;，刚好造成 1 字节越界覆盖。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="strlen"&gt;strlen&#10;&lt;/h2&gt;&lt;h3 id="原型-9"&gt;原型&#10;&lt;/h3&gt;&lt;p&gt;size_t strlen(const char *s)&lt;/p&gt;&#10;&lt;h3 id="逻辑-9"&gt;逻辑&#10;&lt;/h3&gt;&lt;p&gt;顺序遍历字符，计算直到遇到 &lt;code&gt;\0&lt;/code&gt; 前的字符个数&lt;/p&gt;&#10;&lt;h3 id="漏洞存在点-9"&gt;漏洞存在点&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;截断绕过：&lt;/strong&gt; 若用 &lt;code&gt;read&lt;/code&gt; 读取输入后紧接着 &lt;code&gt;strlen(buf)&lt;/code&gt; 做长度校验，若输入开头或中间塞入 &lt;code&gt;\x00&lt;/code&gt;，&lt;code&gt;strlen&lt;/code&gt; 会被提前截断返回极小的值，从而绕过后续长度过滤。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="堆部分函数"&gt;堆部分函数&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;函数&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;底层行为机制&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;漏洞触发场景&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;free(ptr)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;释放内存块，将其链接到 tcache / fastbin / unsorted bin 等链表中。&lt;strong&gt;默认不清空指针&lt;/strong&gt;。&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;产生 &lt;strong&gt;Use-After-Free (UAF)&lt;/strong&gt;（指针未置 NULL 仍可读写）或 &lt;strong&gt;Double Free&lt;/strong&gt;（连续释放同一 chunk 导致 bin 链成环）。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;malloc(size)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;分配指定字节大小的 chunk。&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;传入极端参数（如 &lt;code&gt;size = 0&lt;/code&gt; 或 &lt;code&gt;size = -1&lt;/code&gt; 整数溢出）引发异常行为，或与堆越界结合伪造 chunk 发生 overlap。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;realloc(ptr, size)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;根据新 &lt;code&gt;size&lt;/code&gt; 调整内存块大小；若扩大且原地不足则在别处分配并释放原块。&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;若 &lt;code&gt;realloc&lt;/code&gt; 失败返回 &lt;code&gt;NULL&lt;/code&gt; 易造成悬挂指针；若缩小尺寸但未更新外层代码记录的 &lt;code&gt;len&lt;/code&gt;，可导致堆越界读写。&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;</description></item></channel></rss>