一般的字符,可以直接复制到输出流。
%[parameter][flags][field width][.precision][length]type
Parameter
可以忽略或者n$
使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。 如果任意一个占位符使用了parameter,则其他所有占位符必须也使用parameter
printf("%2$d %2$#x; %1$d %1$#x",16,17)
//"17 0x11; 16 0x10"
flags
字符 | 描述 |
---|---|
+ | 总是表示有符号数值的 + 或 - 号,缺省情况是忽略正数的符号。仅适用于数值类型。 |
空格 | 使得有符号数的输出如果没有正负号或者输出0个字符,则前缀1个空格。如果空格与’+’同时出现,则空格说明符被忽略。 |
- | 左对齐。缺省情况是右对齐。 |
# | 对于’g ‘与’G ‘,不删除尾部0以表示精度。对于’f ’, ‘F ’, ‘e ’, ‘E ’, ‘g ’, ‘G ’, 总是输出小数点。对于’o ’, ‘x ’, ‘X ’, 在非0数值前分别输出前缀0 , 0x , and 0X 表示数制。 |
0 | 如果width选项前缀以0 ,则在左侧用0 填充直至达到宽度要求。例如printf("%2d", 3) 输出” 3 “,而printf("%02d", 3) 输出”03 “。如果0 与- 均出现,则0 被忽略,即左对齐依然用空格填充。 |
width
输出最小宽度,若实际位数多于width,则按照实际输出,若小于,则补空格或0。
Precision
指明输出的最大长度。
对于d、i、u、x、o的整型数值,是指最小数字位数,不足的位要在左侧补0,如果超过也不截断,缺省值为1。
对于a,A,e,E,f,F的浮点数值,是指小数点右边显示的数字位数,必要时四舍五入或补0;缺省值为6。
对于g,G的浮点数值,是指有效数字的最大位数;缺省值为6。
对于s的字符串类型,是指输出的字节的上限,超出限制的其它字符将被截断。如果域宽为*,则由对应的函数参数的值为当前域宽。如果仅给出了小数点,则域宽为0。
Length
指出浮点型参数或整型参数的长度
字符 | 描述 |
---|---|
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 尺寸的整型参数。 |
Type
字符 | 描述 |
---|---|
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,5] 内,输出为定点形式;否则输出为指数浮点形式。’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 * 型 |
a , A | double 型的16进制表示,”[−]0xh.hhhh p±d”。其中指数部分为10进制表示的形式。例如:1025.010输出为0x1.004000p+10。’a ‘使用小写字母,’A ‘使用大写字母。(C++11流使用hexfloat 输出16进制浮点数) |
n | 不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。 |
% | ’% ‘字面值,不接受任何flags, width, precision or length。 |
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 代表一个反斜线字符’’' | 092 |
' | 代表一个单引号(撇号)字符 | 039 |
" | 代表一个双引号字符 | 034 |
\? | 代表一个问号 | 063 |
\0 | 空字符(NUL) | 000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 十六进制 |
函数 | 基本介绍 |
---|---|
printf | 输出到 stdout |
fprintf | 输出到指定 FILE 流 |
vprintf | 根据参数列表格式化输出到 stdout |
vfprintf | 根据参数列表格式化输出到指定 FILE 流 |
sprintf | 输出到字符串 |
snprintf | 输出指定字节数到字符串 |
vsprintf | 根据参数列表格式化输出到字符串 |
vsnprintf | 根据参数列表格式化输出指定字节到字符串 |
setproctitle | 设置 argv |
syslog | 输出日志 |
printf
#include <stdio.h>
int main(void)
{
printf("%d%d%d%d%s",1,2,3,4,"a");
return 0;
}
mov dword ptr [esp+14h], offset aA ; "a"
mov dword ptr [esp+10h], 4
mov dword ptr [esp+0Ch], 3
mov dword ptr [esp+8], 2
mov dword ptr [esp+4], 1
mov dword ptr [esp], offset aDDDDS ; "%d%d%d%d%s"
call _printf
调用printf时栈的布局
在进入 printf 之后,函数首先获取第一个参数(”%d%d%d%d%s”),一个一个读取其字符会遇到两种情况
如果参数数量大于给出的数据数量,printf会按照参数规定的格式逐个输出栈上的数据
#include <stdio.h>
int main(void)
{
printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",1,2,3);
return 0;
}
对于%s,如果提供了一个不可访问的地址,程序就会崩溃
栈上不可能每一格值都对应合法的地址,总会有可以使程序崩溃的值,所以给printf传入若干个%s就可以使程序崩溃
俺寻思这就根据想要的数据传入type就完事了,想搞到指定数据就用Parameter
%p,然后算一波,可以得到栈基址
%s可以获取变量所对应地址的内容,但是有零截断
libc泄露
确定要覆盖的变量地址
确定相对偏移
找到格式化字符串在栈上的位置,计算相对于printf格式化字符串参数的偏移,然后得出格式化字符串中的要覆盖的参数的地址相当于格式化字符串的第几个参数
进行覆盖
#payload
...[overwrite addr]....%[overwrite offset]$n
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).