格式化字符串

格式化字符串:

格式化字符串函数可以接受可变数量的参数,并将第一个字符串作为格式化字符串,然后去解析之后的参数。常见的格式化字符串函数有:

  • scanf
  • printf,输出到stdout
  • fprintf,输出到指定的FILE流
  • vprintf,根据参数列表格式化输出到 stdout
  • vfpirntf,根据参数列表格式化输出到指定 FILE 流
  • sprintf,输出到字符串
  • snprintf,输出指定字节数到字符串
  • vsprintf,根据参数列表格式化输出到字符串
  • vsnprintf,根据参数列表格式化输出指定字节到字符串
  • setproctitle,设置argv
  • syslog,输出日志

 常利用的格式化字符串有:

  • %x,以十六进制输出内存内容
  • %p,以带前缀0x的十六进制输入内存内容
  • %s,泄露指定地址空间的内容
  • %n,向指定地址空间写入以输出的字节数
  • %hn,向指定地址空间写入两个字节的数据,写入的数值是已输出的字节数
  • %hhn,向指定地址空间写入一个字节的数据,写入的数值是已输出的字节数

下面逐个讲解。先看一个例程:

#include <stdio.h>
int main()
{
    char s[100];
    int a=1, b=0x22222222, c=-1;
    scanf("%s", s);
    printf("%04x.%04x.%08x.%s
", a, b, c, s);
    printf(s);
    return 0;
}

运行后的结果如下:

countfatcode@ubuntu:~/Code $ ./formatstring 
yuan
0001.22222222.ffffffff.yuan

接下来调试观察是如何泄露内存的:

gdb-peda$ stack 20
0000| 0xffffd3b0 --> 0x80485d3 ("%04x.%04x.%08x.%s
")
0004| 0xffffd3b4 --> 0x1 
0008| 0xffffd3b8 ("""""37737737737735032337737735032337737730331377367377265", <incomplete sequence 360>)
0012| 0xffffd3bc --> 0xffffffff 
0016| 0xffffd3c0 --> 0xffffd3e8 ("yuan")
0020| 0xffffd3c4 --> 0xffffd3e8 ("yuan")
0024| 0xffffd3c8 --> 0xf7ffd918 --> 0x0 
0028| 0xffffd3cc --> 0xf0b5ff 
0032| 0xffffd3d0 --> 0xffffd40e --> 0xffff0000 --> 0x0 
0036| 0xffffd3d4 --> 0x1 
0040| 0xffffd3d8 --> 0xc2 
0044| 0xffffd3dc --> 0x1 
0048| 0xffffd3e0 ("""""377377377377yuan")
0052| 0xffffd3e4 --> 0xffffffff 
0056| 0xffffd3e8 ("yuan")
0060| 0xffffd3ec --> 0x0 
0064| 0xffffd3f0 --> 0xf7ffd000 --> 0x23f40 
0068| 0xffffd3f4 --> 0xf7ffd918 --> 0x0 
0072| 0xffffd3f8 --> 0xffffd410 --> 0xffffffff 
0076| 0xffffd3fc --> 0x804827d ("__libc_start_main")
gdb-peda$ ni
0001.22222222.ffffffff.yuan

观察上图栈结构可知程序把printf函数的第一个参数的首地址压入栈中,然后根据格式化字符串依次解析栈中除栈顶中的内容。

原文地址:https://www.cnblogs.com/countfatcode/p/11796479.html