目录

一、类printf函数簇实现原理

二、格式化字符串攻击原理

三、一个实际的例子


一、类printf函数簇实现原理

类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型。

对于这种情况,可以使用省略号指定参数表。

带有省略号的函数定义中,参数表分为两部分,前半部分是确定个数、确定类型的参数,第二部分就是省略号,代表数目和类型都不确定的参数表,省略号参数表中参数的个数和参数的类型是事先的约定计算出来的,每个实参的地址(指针)是根据确定参数表中最后一个实参的地址算出来的。

这里涉及到函数调用时的栈操作。函数栈的栈底是高地址,栈顶是底地址。在函数调用

时函数实参是从最后一个参数(最右边的参数)到第一个参数(最左边的参数)依次被压入栈顶方向。也就是说函数调用时,函数实参的地址是相连的,并且从左到右地址是依次增加的。如:

#include <stdio.h>
#include <stdlib.h>  void fun(int a, ...)
{  int i;  int *temp = &a;  temp++;  for (i = 0; i < a; ++i)  {  printf("%d ",*temp);  temp++;  }  printf("/n");
}  int main()
{  int a = 1;  int b = 2;  int c = 3;  int d = 4;  fun(4, a, b, c, d);  return 0;
}   

在上面的例子中,void fun(int a, ...)函数约定第一个确定参数表示省略号参数表中参数的个数,省略号参数表中的参数全都是int 类型的,这样fun函数就可以正常工作了。

类printf函数簇的工作原理和fun函数是一样的,只不过更为复杂和精巧。

如printf的函数形式为 int printf(const char *fmt, …)。

由于printf函数实现的功能比较复杂,我们来看一个我们自己实现的myprintf函数,改函数不涉及低层系统io操作。

#include <stdio.h>
#include <stdlib.h>  void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型
{  char* pArg=NULL; //等价于printf原始实现的va_list  char c;  pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值  pArg += sizeof(fmt); //等价于原来的va_start  do  {  c =*fmt;  if (c != '%')  {  putchar(c); //照原样输出字符  }  else  {  //按格式字符输出数据  switch(*++fmt)  {  case 'd':  printf("%d",*((int*)pArg));  break;  case 'x':  printf("%#x",*((int*)pArg));  break;  default:  break;  }  pArg += sizeof(int); //等价于原来的va_arg  }  ++fmt;  }while (*fmt != '/0');  pArg = NULL; //等价于va_end  return;
}  int main(int argc, char* argv[])
{  int i = 1;  int j = 2;  myprintf("the first test:i=%d/n",i,j);  myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);   return 0;
}  

myprintf函数中也有类似的约定,确定参数表中最后一个参数是一个const char* 类型的字符串,在这个字符串中出现“%d”和“%x”次数的和就是省略号参数表中参数的个数,省略号参数表中的参数类型也都是int类型。

同样的,实际的printf函数也有这样的约定:确定参数表中最后一个参数是一个const char* 类型的字符串,省略号参数表中参数个数就是这个字符串中出现的“%d”,“%x”,“%s”…次数的和,省略号参数表中参数的类型也是由“%d”,“%x”,“%s”……等格式化字符来指示的。

因此,类printf函数中省略号参数表中参数的个数和类型都是由类printf函数中的那个格式化字符串来决定的。

二、格式化字符串攻击原理

因为类printf函数中省略号参数表中参数的个数和类型都是由类printf函数中的那个格式化字符串来决定的,所以攻击者可以利用编程者的疏忽或漏洞,巧妙构造格式化字符串,达到攻击目的。

如果一个程序员的任务是:打印输出一个字符串或者把这个串拷贝到某缓冲区内。他可以写出如下的代码:printf("%s", str);但是为了节约时间和提高效率,并在源码中少输入6个字节,他会这样写:printf(str);

为什么程序员写的是错误的呢?他传入了一个他想要逐字打印的字符串。实际上该字符串被printf函数解释为一个格式化字符(formatstring),printf就会根据该字符串来决定printf函数中省略号参数表中参数的格式和类型,如果这个程序员想要打印的字符串中刚好有“%d”,“%x”之类的格式化字符,那么一个变量的参数值就从堆栈中取出。

比如:

#include <stdio.h>
#include <stdlib.h>   int main(int argc, char* argv[])
{  if(argc != 2)  return 0;  printf(argv[1]);  return 0;
}

当./a.out “hello world”时一切正常,但是当./a.out “%x”时,就会有莫名其妙的数字被打印出来了。

很明显,攻击者至少可以通过打印出堆栈中的这些值来偷看程序的内存。但是有些事情就不那么明显了,这个简单的错误允许向运行中程序的内存里写入任意值。

printf有一个比较另类的用法:%n,当在格式化字符串中碰到"%n"的时候,在%n域之前输出的字符个数会保存到下一个参数里。例如,为了获取在两个格式化的数字之间空间的偏量:

int main(int argc, char* argv[])
{  int pos, x = 235, y = 93;  printf("%d %n%d/n", x, &pos, y);  printf("The offset was %d/n", pos);  return 0;
}

输出4(“235 ”的长度)

%n格式返回应该被输出的字符数目,而不是实际输出的字符数目。当把一个字符串格式化输出到一个定长缓冲区内时,输出字符串可能被截短。不考虑截短的影响,%n格式表示如果不被截短的偏量值(输出字符数目)。为了说明这一点,下面的代码会输出100而不是20:

int main()
{  char buf[20];  int pos, x = 0;  snprintf(buf, sizeof(buf), "%.100d%n", x, &pos);  printf("position: %d/n", pos);  return 0;
}

而%n和%d,%x,%s的显著的不同就是%n是会改变变量的值的,这也就是格式化字符串攻击的爆破点。

三、一个实际的例子

下面这个例子至少可以X86的Redhat和arch Linux下面进行演示。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>  char daddr[16];  int main(int argc, char **argv)
{  char buf[100];  int x;  x = 1;  memset(daddr,'/0',16);  printf("before format string x is %d/%#x (@ %p)/n", x, x, &x);  strncpy(daddr,"PPPPPPP%n",9);         snprintf(buf,sizeof(buf),daddr);   //实施格式化字符串攻击  buf[sizeof(buf) - 1] = 0;  printf("after format string x is %d/%#x (@ %p)/n", x, x, &x);  return 0;
}

运行的结果是:x被成功的改成了7。

上面的例子利用了linux函数调用时的内存残像,来实现格式化字符串攻击的。(参考的经典文章是用猜地址的方法来实现的,猜的一头雾水)

这里我们来分析一下main函数中的堆栈变化情况:

如上图所示,在调用snprintf函数之前,首先调用了printf函数,printf的函数第四个参数是&x,这样在main函数的堆栈内存中留下了&x的内存残像。当调用snprintf时,系统本来只给snprintf准备了3个参数,但是由于格式化字符串攻击,使得snprinf认为应该有四个参数传给它,这样snprintf就私自把&x的内存残像作为第4个参数读走了,而snprintf所谓的第4个参数对应的“%n”,于是snprintf就成功的修改了变量x的值。

而在实际网络环境中可利用的格式化字符串攻击也是很多的。下图就是一个实际网络攻击的截图。

c语言printf相关函数 格式化字符串攻击 简介相关推荐

  1. c语言中的格式化字符串

    C语言中格式字符串的一般形式为:  % [ 标志 ][ 输出最小宽度 ][. 精度 ][ 长度 ] 类型 , 其中方括号[]中的项为可选项. 一.类型 我们用一定的字符用以表示输出数据的类型,其格式符 ...

  2. pwn - 格式化字符串攻击

    前言 日前入门pwn,粗浅学习了写shellcode,rop,栈溢出攻击,现在遇到了fmtstr,把我头搞炸了,决定好好学习一下,写一篇文章作为记录,供日后参考 样本 题目:https://ctf.s ...

  3. C语言编程之格式化输出函数printf()说明

    格式化字符串,是一些程序设计语言在格式化输出API函数中用于指定输出参数的格式与相对位置的字符串参数,例如C.C++等程序设计语言的printf类函数,其中的转换说明用于把随后对应的0个或多个函数参数 ...

  4. c语言printf输出字母,C语言printf()格式化输出控制

    printf()函数是格式化输出函数,一般用于向标准输出设备按规定格式输出信息.在编写程序时经常会用到此函数.printf()函数的调用格式为: printf("", ); 其中格 ...

  5. c语言 字符串 格式化,c的格式化字符串

    一.printf()函数 printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出 信息.在编写程序时经常会用到此函数.printf()函数的调用格式为: printf(" ...

  6. bootstrap table格式化字符串_你要的PHP字符串处理函数都在这了!

    php中文网最新课程 每日17点准时技术干货分享 打印输出函数 说明 echo 输出一个或多个字符串 print 输出字符串 printf 输出格式化字符串 sprintf Return a form ...

  7. 武林c语言,c语言printf函数的使用

    看C语言程序的时候,往往会遇到printf函数输出,在此,武林小编我就总结一下c语言printf函数的使用,本文对c语言中printf函数的使用进行了详细的分析介绍,需要的朋友参考下. printf的 ...

  8. C语言Printf格式大全(各种%输出形式)

    C语言Printf格式 C中格式字符串的一般形式为: %[标志][输出最小宽度][.精度][长度]类型, 其中方括号[]中的项为可选项.各项的意义介绍如下: 1.类型类型字符用以表示输出数据的类型,其 ...

  9. c++字符串输入_【pwn】什么是格式化字符串漏洞?

    0x00 前言 格式化字符串漏洞是在CWE[1](Common Weakness Enumeration,通用缺陷枚举)例表中的编号为CWE-134,由于在审计过程中很容易发现该漏洞,所以此类漏洞很少 ...

最新文章

  1. GRE核心词汇助记与精练-List12转
  2. 数据加密类型及创建和申请CA证书
  3. bootstraptable控制分页_bootstrap table分页(前后端两种方式实现)
  4. 向上弹出菜单jQuery插件
  5. 数据科学入门与实战:玩转pandas之七数据透视
  6. 201509-1-数列分段
  7. python开发k8s管理平台_将Python项目部署到Kubernetes
  8. 简历制作器App使用条款
  9. windows存储空间清理,C盘空间清理教程,磁盘清理方法
  10. jvm System.gc()说明
  11. Bandwagon centos 6 32位配置SVN,apache,mysql和php
  12. Unity 相机固定角度平移至指定物体
  13. 电赛校赛总结----一维板球系统【代码开源】
  14. “空天地海”一体化的海上应急通信网络技术综述
  15. python输入数学表达式并求值_Python 条件表达式求值
  16. JS实战面向对象 - 贪吃蛇
  17. 网络营销好学吗?自学和培训该怎么选?
  18. 公司破产,创始人自杀,曾号称要开5000家门店的咖啡厅,如今却败走中国
  19. RichClient/RIA原则与实践(上)
  20. android 绘制正方形图片,是Android的自定义View-绘制流程-正方形图片控件(SquareImageView)...

热门文章

  1. 給曾經的三年,寫點什么吧。-紀念華軟
  2. Kubernetes — Kubespray 开箱即用的部署工具
  3. 互联网协议 — FTP 文件传输协议
  4. 2018年04月26日交的智商税
  5. java循环练习:水仙花数
  6. 关于mysql的调优
  7. ActiveReports 报表应用教程 (2)---清单类报表
  8. Google正式将网速列为网站排名因素
  9. 【发现】彻底清除www.go2000.cc的清除方法
  10. rhel5.1 vncserver