# 格式化字符串漏洞
## 1、什么是格式化字符串
要研究格式化字符串漏洞首先我们必须得明白什么是格式化字符串。
我们用c语言中常见的printf来举例
```bash
printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出
信息。在编写程序时经常会用到此函数。函数的原型为:
`int printf(const char *format, ...)`;
函数返回值为整型。若成功则返回输出的字符数,输出出错则返回负值。
printf()函数的调用格式为:
`printf("<格式化字符串>", <参量表>);`
其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原
样输出; 另一部分是格式化规定字符, 以"%"开始, 后跟一个或几个规定字符,
用来确定输出内容格式。
参量表是需要输出的一系列参数, 其个数必须与格式化字符串所说明的输出
参数个数一样多, 各参数之间用","分开, 且顺序一一对应, 否则将会出现意想
不到的错误。
```
看的有些模糊是吧,没关系。总的来说就是`printf`这个函数的第一个参数(第一个逗号前面的东西)就叫做格式化字符串。
它是由两部分组成:
- 格式化说明符:特点明显,“%“号开头,后面紧跟几个字符,例如:
```bash
`
%d - 十进制 - 输出十进制整数
%s - 字符串 - 从内存中读取字符串
%x - 十六进制 - 输出十六进制数
%c - 字符 - 输出字符
%p - 指针 - 指针地址
%n - 到目前为止所写的字符数
`
```
+ 普通字符串
#### 例子
我们拿个例子来说
```cpp
#include <stdio.h>
int main(void){
printf("My name is %s","BurYiA");
return 0;
}
```
在这个的**printf**中,“My name is ”就是普通字符串,而“%s”则是格式化说明符。学过c语言的应该知道,这个程序的执行结果是会打印一行
```
My name is BurYiA
```
很明显,格式化说明符将后面的参数以特定格式替换到了格式化说明符的位置
### 重点
那么问题来了,为什么我们要这么详细的说明这个东西呢。不知道大家有没有发现我上面写的一个格式化说明符“**%n**”,**它的功能是将%n之前打印出来的字符个数,赋值给一个变量**
例如:
```cpp
#include <stdio.h>
int main(void)
{
int c = 0;
printf("The number is %n", &c);
printf("%dn", c);
return 0;
}
```
你能猜一下它的结果么?
好吧,咱们不猜它,有图有真相,直接看结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191023182621630.png)
好了,暂时先说到这,其他的你们可以先自由的发挥一下想象
## 2、漏洞点
一般情况我们用**printf**是这样用的
```cpp
#include <stdio.h>
int main()
{
char arr[100];
scanf("%s", arr);
printf("%s", arr);
return 0;
}
```
可是,不知道有没有人试过这样写:
```cpp
#include <stdio.h>
int main()
{
char arr[100];
scanf("%s", arr);
printf(arr);
return 0;
}
```
这样写对不对呢?我们来运行一下试试
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191023211000631.png)
咳咳,有些小意外,**scanf**输入的时候遇到空格就会停止,所以后面的就没有输入进去,但……无妨,很明显的可以看到,它成功的输出了。
脑子转的快的可能已经想到了,这个时候我们就可以控制它的格式化字符串,使它变成我们想要的东西。