格式化字符串
CPU利用ebp访问栈内东西
参数从右向左入栈
printf函数参数从右边向左入栈
%[flags][width][.precision][length]specifier
说明符(specifier)用于规定输出数据的类型
标志(flags)用于规定输出样式
最小宽度(width)用于控制显示字段的宽度
#include <stdio.h>
#define PAGES 931
int main() {
printf("*%2d*\n", PAGES); //输出的字段长度大于最小宽度,不会截断输出
printf("*%10d*\n", PAGES); //默认右对齐,左边补空格
printf("*%*d*\n", 2, PAGES); //等价于 printf("*%2d*\n",PAGES)
return 0;
}
//程序运行结果:
*931*
* 931*
*931*
精度(.precision)用于指定输出精度
对于整数说明符(d、i、o、u、x、X):precision 指定了要打印的数字的最小位数。如果写入的值短于该数,结果会用前导零来填充。如果写入的值长于该数,结果不会被截断。精度为 0 意味着不写入任何字符;
对于 e、E 和 f 说明符:要在小数点后输出的小数位数;
#include <stdio.h>
int main() {
const double RENT = 3852.99; // const-style constant
printf("*%4.2f*\n", RENT);
printf("*%3.1f*\n", RENT);
printf("*%10.3f*\n", RENT);
return 0;
}
//程序运行结果:
*3852.99*
*3853.0*
* 3852.990*
类型长度(length)用于控制待输出数据的数据类型长度
查看内存内容
format参数之上为参数依次上推
%x十六进制输出
写入内存
%n把前面已经打印的长度写入某个内存地址
print("%d %n",a,&a)
如果a=1234 那么为a会变成5 因为空格也算
自定义写入内存值
print("%5d%n",a,&a)
或
print("%.5d%n",a,&a)
%n$p
代表格式化字符串的第n个参数 不包括格式化字符串 位置以上推
建议%p获取值
[某明显的字符串]%p%p%p%p……
观察printf(s)结果找到第几个参数为s 则可修改为某个函数的got地址进行 输入 got地址值%n$s 可打印出真实地址
覆盖内存
某地址+%Nd +%A$n
往第A个参数中写入总长度(某地址或其他的+N)
用小数字覆盖
更换顺序 [某长度东西]+%A$n
用大数字覆盖
小端法存储:低地址存低字节
一个一个字节的覆盖
%A$hhn:取前面总长度的低位一个字节的值写到第A个参数
格式化字符串与malloc free有关的格式化字符串漏洞
malloc()、calloc()、realloc()分配堆中空间
alloca()来分配栈中的空间
scanf输入和printf输出的字符过多的时候,会调用malloc来处理,且结束之后都会调用free。
所以当输入或输出较多的时候,可以覆盖_free_hook或者_malloc_hook为其他函数地址来调用其他函数
格式化字符串漏洞工具
这个是专门为32位程序格式化字符串漏洞输出payload的一个函数
fmtstr_payload(offset(格式化字符串在函数参数中的偏移),{addr: value1, addr2: value2}) 用于往addr里写入value的值(常用:{printf_got})
64位
参数安装寄存器存放,大于六个时放入栈中
此时%np的第几个参数依然是从格式化字符串后的参数算起