This commit is contained in:
firmianay 2017-08-18 23:34:44 +08:00
parent 939db92dd5
commit afd013f5ec
2 changed files with 130 additions and 0 deletions

View File

@ -3,6 +3,7 @@
- [从源代码到可执行文件](#从源代码到可执行文件)
- [C 语言标准库](#c-语言标准库)
- [整数表示](#整数表示)
- [格式化输出函数](#格式化输出函数)
## 从源代码到可执行文件
@ -168,3 +169,114 @@ $ cat /usr/include/limits.h
```
了解整数的符号和大小是很有用的,在后面的相关章节中我们会介绍整数溢出的内容。
## 格式化输出函数
#### 格式化输出函数
C 标准中定义了下面的格式化输出函数(参考 `man printf`
```text
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
#include <stdarg.h>
int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vdprintf(int fd, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
```
- `fprintf()` 按照格式字符串的内容将输出写入流中。三个参数为流、格式字符串和变参列表。
- `printf()` 等同于 `fprintf()`,但是它假定输出流为 `stdout`
- `sprintf()` 等同于 `fprintf()`,但是输出不是写入流而是写入数组。在写入的字符串末尾必须添加一个空字符。
- `snprintf()` 等同于 `sprintf()`,但是它指定了可写入字符的最大值 `size`。当 `size` 大于零时,输出字符超过第 `size-1` 的部分会被舍弃而不会写入数组中,在写入数组的字符串末尾会添加一个空字符。
- `dprintf()` 等同于 `fprintf()`,但是它输出不是流而是一个文件描述符 `fd`
- `vfprintf()`、`vprintf()`、`vsprintf()`、`vsnprintf()`、`vdprintf()` 分别与上面的函数对应,只是它们将变参列表换成了 `va_list` 类型的参数。
#### 格式字符串
格式字符串是由普通字符ordinary character包括 `%`和转换规则conversion specification构成的字符序列。普通字符被原封不动地复制到输出流中。转换规则根据与实参对应的转换指示符对其进行转换然后将结果写入输出流中。
一个转换规则有可选部分和必需部分组成:
```text
%[ 参数 ][ 标志 ][ 宽度 ][ .精度 ][ 长度 ] 转换指示符
```
- (必需)转换指示符
字符 | 描述
--- | ---
`d`, `i` | 有符号十进制数值 `int`。'`%d`' 与 '`%i`' 对于输出是同义;但对于 `scanf()` 输入二者不同,其中 `%i` 在输入值有前缀 `0x``0` 时,分别表示 16 进制或 8 进制的值。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。
`u` | 十进制 `unsigned int`。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。
`f`, `F` | `double` 型输出 10 进制定点表示。'`f`' 与 '`F`' 差异是表示无穷与 NaN 时,'`f`' 输出 '`inf`', '`infinity`' 与 '`nan`''`F`' 输出 '`INF`', '`INFINITY`' 与 '`NAN`'。小数点后的数字位数等于精度,最后一位数字四舍五入。精度默认为 6。如果精度为 0 且没有 # 标记,则不出现小数点。小数点左侧至少一位数字。
`e`, `E` | `double` 值,输出形式为 10 进制的([`-`]d.ddd `e`[`+`/`-`]ddd). `E` 版本使用的指数符号为 `E`(而不是`e`)。指数部分至少包含 2 位数字,如果值为 0则指数部分为 00。Windows 系统,指数部分至少为 3 位数字,例如 1.5e002,也可用 Microsoft 版的运行时函数 `_set_output_format` 修改。小数点前存在 1 位数字。小数点后的数字位数等于精度。精度默认为 6。如果精度为 0 且没有 # 标记,则不出现小数点。
`g`, `G` | `double` 型数值,精度定义为全部有效数字位数。当指数部分在闭区间 [-4,精度] 内,输出为定点形式;否则输出为指数浮点形式。'`g`' 使用小写字母,'`G`' 使用大写字母。小数点右侧的尾数 0 不被显示;显示小数点仅当输出的小数部分不为 0。
`x`, `X` | 16 进制 `unsigned int`。'`x`' 使用小写字母;'`X`' 使用大写字母。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。
`o` | 8 进制 `unsigned int`。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。
`s` | 如果没有用 `l` 标志,输出 `null` 结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了 `l` 标志,则对应函数参数指向 `wchar_t` 型的数组,输出时把每个宽字符转化为多字节字符,相当于调用 `wcrtomb` 函数。
`c` | 如果没有用 `l` 标志,把 `int` 参数转为 `unsigned char` 型输出;如果用了 `l` 标志,把 `wint_t` 参数转为包含两个元素的 `wchart_t` 数组,其中第一个元素包含要输出的字符,第二个元素为 `null` 宽字符。
`p` | `void *` 型,输出对应变量的值。`printf("%p", a)` 用地址的格式打印变量 `a` 的值,`printf("%p", &a)` 打印变量 `a` 所在的地址。
`a`, `A` | `double` 型的 16 进制表示,"[]0xh.hhhh p±d"。其中指数部分为 10 进制表示的形式。例如1025.010 输出为 0x1.004000p+10。'`a`' 使用小写字母,'`A`' 使用大写字母。
`n` | 不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
`%` | '`%`' 字面值,不接受任何除了 `参数` 以外的部分。
- (可选)参数
字符 | 描述
--- | ---
`n$` | `n` 是用这个格式说明符显示第几个参数;这使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。如果任意一个占位符使用了 `参数`,则其他所有占位符必须也使用 `参数`。例:`printf("%2$d %2$#x; %1$d %1$#x",16,17)` 产生 "`17 0x11; 16 0x10`"
- (可选)标志
字符 | 描述
--- | ---
`+` | 总是表示有符号数值的 '`+`' 或 '`-`' 号,缺省情况是忽略正数的符号。仅适用于数值类型。
*空格* | 使得有符号数的输出如果没有正负号或者输出 0 个字符,则前缀 1 个空格。如果空格与 '`+`' 同时出现,则空格说明符被忽略。
`-` | 左对齐。缺省情况是右对齐。
`#` | 对于 '`g`' 与 '`G`',不删除尾部 0 以表示精度。对于 '`f`', '`F`', '`e`', '`E`', '`g`', '`G`', 总是输出小数点。对于 '`o`', '`x`', '`X`', 在非 0 数值前分别输出前缀 `0`, `0x``0X`表示数制。
`0` | 如果 `宽度` 选项前缀为 `0`,则在左侧用 `0` 填充直至达到宽度要求。例如 `printf("%2d", 3)` 输出 "`3`",而 `printf("%02d", 3)` 输出 "`03`"。如果 `0``-` 均出现,则 `0` 被忽略,即左对齐依然用空格填充。
- (可选)宽度
是一个用来指定输出字符的最小个数的十进制非负整数。如果实际位数多于定义的宽度,则按实际位数输出;如果实际位数少于定义的宽度则补以空格或 0。
- (可选)精度
精度是用来指示打印字符个数、小数位数或者有效数字个数的非负十进制整数。对于 `d`、`i`、`u`、`x`、`o` 的整型数值,是指最小数字位数,不足的位要在左侧补 0如果超过也不截断缺省值为 1。对于 `a`, `A`, `e`, `E`, `f`, `F` 的浮点数值,是指小数点右边显示的数字位数,必要时四舍五入;缺省值为 6。对于 `g`, `G` 的浮点数值,是指有效数字的最大位数。对于 `s` 的字符串类型,是指输出的字节的上限,超出限制的其它字符将被截断。如果域宽为 `*`,则由对应的函数参数的值为当前域宽。如果仅给出了小数点,则域宽为 0。
- (可选)长度
字符 | 描述
--- | ---
`hh` | 对于整数类型,`printf` 期待一个从 `char` 提升的 `int` 整型参数。
`h` | 对于整数类型,`printf` 期待一个从 `short` 提升的 `int` 整型参数。
`l` | 对于整数类型,`printf` 期待一个 `long` 整型参数。对于浮点类型,`printf` 期待一个 `double` 整型参数。对于字符串 `s` 类型,`printf` 期待一个 `wchar_t` 指针参数。对于字符 `c` 类型,`printf` 期待一个 `wint_t` 型的参数。
`ll` | 对于整数类型,`printf` 期待一个 `long long` 整型参数。Microsoft 也可以使用 `I64`
`L` | 对于浮点类型,`printf` 期待一个 `long double` 整型参数。
`z` | 对于整数类型,`printf` 期待一个 `size_t` 整型参数。
`j` | 对于整数类型,`printf` 期待一个 `intmax_t` 整型参数。
`t` | 对于整数类型,`printf` 期待一个 `ptrdiff_t` 整型参数。
#### 例子
```c
printf("Hello %%"); // "Hello %"
printf("Hello world!"); // "Hello world!"
printf("Number: %d", 123); // "Number: 123"
printf("%s %s", "Format", "Strings"); // "Format Strings"
printf("%12c", 'A'); // " A"
printf("%16s", "Hello"); // " Hello!"
int n;
printf("%12c%n", 'A', &n); // n = 12
printf("%16s%n", "Hello!", &n); // n = 16
printf("%2$s %1$s", "Format", "Strings"); // "Strings Format"
printf("%42c%1$n", &n); // 首先输出41个空格然后输出 n 的低八位地址作为一个字符
```
这里我们对格式化输出函数和格式字符串有了一个详细的认识,后面的章节中我们会介绍格式化字符串漏洞的内容。

View File

@ -1 +1,19 @@
# 格式化字符串漏洞
- [格式化输出函数和格式字符串](#格式化输出函数和格式字符串)
- [格式化字符串漏洞基本原理](#格式化字符串漏洞基本原理)
- [格式化字符串漏洞示例](#格式化字符串漏洞示例)
- [CTF 中的格式化字符串漏洞](#ctf-中的格式化字符串漏洞)
## 格式化输出函数和格式字符串
在 C 语言基础章节中,我们详细介绍了格式化输出函数和格式化字符串的内容。在开始探索格式化字符串漏洞之前,强烈建议回顾该章节。
## 格式化字符串漏洞基本原理
## 格式化字符串漏洞示例
## CTF 中的格式化字符串漏洞