格式化字符串
格式化字符串是一种用于将变量或值插入到字符串中的方法。它允许我们在字符串中使用占位符,然后通过传递参数来替换这些占位符。在很多编程语言中,格式化字符串通常使用特定的语法或函数来实现。
在Python中,格式化字符串可以使用百分号(%)操作符或者字符串的format()方法来实现。使用百分号操作符时,我们可以在字符串中使用占位符(如%s、%d等),然后通过一个元组或字典来传递参数。例如:
name = "Alice"
age = 25
print("My name is %s and I am %d years old." % (name, age))
使用format()方法时,我们可以在字符串中使用花括号({})作为占位符,并通过传递参数来替换这些占位符。例如:
name = "Alice"
age = 25
print("My name is {} and I am {} years old.".format(name, age))
这样就可以将变量或值动态地插入到字符串中,使得输出更加灵活和可读。
常见的格式化字符串用法
%d - 十进制 - 打印十进制整数
%s - 字符串 - 打印参数地址处的字符串
%x - 十六进制 - 打印十六进制数
%o - 八进制 -打印八进制整形
%c - 字符 - 打印字符
%p - 指针 - 打印指针地址 即void *
%n - 把已经成功输出的字符个数写入对应的整型指针参数所指的变量。将栈上的内容作为地址解析,然后改变这个地址上的内容,写四字节
格式化字符串函数
格式化字符串函数:格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。
格式化字符串漏洞
eg:
正确代码:
#include <stdio.h>
int main()
{
char str[100];
scanf("%s",str);
printf("%s",str);
return 0;
}
错误代码:
#include <stdio.h>
int main()
{
char str[]="qwer";
printf(str);
return 0;
}
此时我们若输入字符串(如AAAA %08x……),输出的是内存中的数据地址。
具体原理(以printf为例):当printf在输出格式化字符串的时候,会维护一个内部指针,当printf逐步将格式化字符串的字符打印到屏幕,当遇到%的时候,printf会期望它后面跟着一个格式字符串,因此会递增内部字符串以抓取格式控制符的输入值。这就是问题所在,printf无法知道栈上是否放置了正确数量的变量供它操作,如果没有足够的变量可供操作,而指针按正常情况下递增,就会产生越界访问。甚至由于%n的问题,可导致任意地址读写。所以尽管没有参数或者参数不足,代码也会将 格式化字符串 后面的内存当做参数以16进制输出。这样就会造成内存泄露。
例题 pwn5
1.checksec查保护
32位文件,开启了canary保护(对栈的保护)
2.将文件拖入ida,F5查看伪代码
此代码显示,只要我们输入的密码与unk_804C044地址存储的数据一致,就可getshell,该地址处于bss段,我们不知道数据是多少。
3.执行一下该文件,找出偏移量
AAAA的ASCII码值为41414141,偏移量为10
4.找到unk_804C044的地址,用%n修改此处地址的值,然后使输入与修改的值相同即可
5.脚本
6.