格式化字符串攻击原理及示例

格式化字符串攻击原理及示范

格式化字符串攻击原理及示范

一、类printf函数簇达成原理

类printf函数的最大的风味正是,在函数定义的时候无法掌握函数实参的多寡和花色。

对此那种处境,能够利用省略号钦点参数表。

带有省略号的函数定义中,参数表分为两有的,前半局部是明确个数、明确项目的参数,第2片段便是省略号,代表数量和品种都不明确的参数表,省略号参数表中参数的个数和参数的品类是先行的约定计算出来的,每种实参的地址(指针)是按照规定参数表中最后1个实参的地点算出来的。

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

时函数实参是从最后一个参数(最左侧的参数)到第2个参数(最右边的参数)依次被压入栈顶方向。也便是说函数调用时,函数实参的地方是绵绵的,并且从左到右地址是各类扩展的。如:

 1 #include <stdio.h>  
 2 #include <stdlib.h>  
 3   
 4 void fun(int a, ...)  
 5 {  
 6     int i;  
 7     int *temp = &a;  
 8     temp++;  
 9     for (i = 0; i < a; ++i)  
10     {  
11         printf("%d ",*temp);  
12         temp++;  
13     }  
14     printf("/n");  
15 }  
16   
17 int main()  
18 {  
19     int a = 1;  
20     int b = 2;  
21     int c = 3;  
22     int d = 4;  
23     fun(4, a, b, c, d);  
24     return 0;  
25 }   

在地点的例证中,void fun(int a,
…)函数约定第3个规定参数表示省略号参数表中参数的个数,省略号参数表中的参数全都以int
类型的,那样fun函数就足以正常职业了。

类printf函数簇的劳作规律和fun函数是1致的,只可是更为复杂和Mini。

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

由于printf函数达成的效益比较复杂,大家来看三个大家自身完成的myprintf函数,改函数不涉及低层系统io操作。

 1 #include <stdio.h>  
 2 #include <stdlib.h>  
 3   
 4 void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型  
 5 {  
 6     char* pArg=NULL; //等价于printf原始实现的va_list  
 7     char c;  
 8     pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值  
 9      pArg += sizeof(fmt); //等价于原来的va_start  
10   
11     do  
12     {  
13         c =*fmt;  
14         if (c != '%')  
15         {  
16             putchar(c); //照原样输出字符  
17           }  
18         else  
19         {  
20             //按格式字符输出数据  
21                switch(*++fmt)  
22             {  
23                 case 'd':  
24                     printf("%d",*((int*)pArg));  
25                     break;  
26                 case 'x':  
27                     printf("%#x",*((int*)pArg));  
28                     break;  
29                 default:  
30                     break;  
31             }  
32             pArg += sizeof(int); //等价于原来的va_arg  
33         }  
34         ++fmt;  
35     }while (*fmt != '/0');  
36     pArg = NULL; //等价于va_end  
37     return;  
38 }  
39   
40 int main(int argc, char* argv[])  
41 {  
42     int i = 1;  
43     int j = 2;  
44     myprintf("the first test:i=%d/n",i,j);  
45     myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);   
46     return 0;  
47 }  

myprintf函数中也有接近的预订,分明参数表中最终三个参数是3个const char*
类型的字符串,在这么些字符串中出现“%d”和“%x”次数的和就是归纳号参数表中参数的个数,省略号参数表中的参数类型也都以int类型。

同等的,实际的printf函数也有如此的预约:明确参数表中最终三个参数是三个const
char*
类型的字符串,省略号参数表中参数个数正是以此字符串中出现的“%d”,“%x”,“%s”…次数的和,省略号参数表中参数的花色也是由“%d”,“%x”,“%s”……等格式化字符来提示的。

所以,类printf函数中省略号参数表中参数的个数和类别都是由类printf函数中的那几个格式化字符串来支配的。

壹、类printf函数簇实现原理

类printf函数的最大的特点就是,在函数定义的时候不能知晓函数实参的数码和类型。

对于那种意况,能够应用省略号内定参数表。

带有省略号的函数定义中,参数表分为两有的,前半有的是规定个数、明确项目标参数,第一片段就是省略号,代表数量和连串都不鲜明的参数表,省略号参数表中参数的个数和参数的连串是先期的预约计算出来的,每一种实参的地点(指针)是基于规定参数表中最后一个实参的地方算出来的。

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

时函数实参是从最后三个参数(最左侧的参数)到第3个参数(最右侧的参数)依次被压入栈顶方向。也正是说函数调用时,函数实参的地方是绵绵的,并且从左到右地址是各种扩大的。如:

 1 #include <stdio.h>  
 2 #include <stdlib.h>  
 3   
 4 void fun(int a, ...)  
 5 {  
 6     int i;  
 7     int *temp = &a;  
 8     temp++;  
 9     for (i = 0; i < a; ++i)  
10     {  
11         printf("%d ",*temp);  
12         temp++;  
13     }  
14     printf("/n");  
15 }  
16   
17 int main()  
18 {  
19     int a = 1;  
20     int b = 2;  
21     int c = 3;  
22     int d = 4;  
23     fun(4, a, b, c, d);  
24     return 0;  
25 }   

在上头的例证中,void fun(int a,
…)函数约定第3个规定参数表示省略号参数表中参数的个数,省略号参数表中的参数全都以int
类型的,那样fun函数就足以健康办事了。

类printf函数簇的行事规律和fun函数是同样的,只不过更为复杂和小巧。

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

是因为printf函数达成的职能相比复杂,我们来看三个大家温馨达成的myprintf函数,改函数不关乎低层系统io操作。

 1 #include <stdio.h>  
 2 #include <stdlib.h>  
 3   
 4 void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型  
 5 {  
 6     char* pArg=NULL; //等价于printf原始实现的va_list  
 7     char c;  
 8     pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值  
 9      pArg += sizeof(fmt); //等价于原来的va_start  
10   
11     do  
12     {  
13         c =*fmt;  
14         if (c != '%')  
15         {  
16             putchar(c); //照原样输出字符  
17           }  
18         else  
19         {  
20             //按格式字符输出数据  
21                switch(*++fmt)  
22             {  
23                 case 'd':  
24                     printf("%d",*((int*)pArg));  
25                     break;  
26                 case 'x':  
27                     printf("%#x",*((int*)pArg));  
28                     break;  
29                 default:  
30                     break;  
31             }  
32             pArg += sizeof(int); //等价于原来的va_arg  
33         }  
34         ++fmt;  
35     }while (*fmt != '/0');  
36     pArg = NULL; //等价于va_end  
37     return;  
38 }  
39   
40 int main(int argc, char* argv[])  
41 {  
42     int i = 1;  
43     int j = 2;  
44     myprintf("the first test:i=%d/n",i,j);  
45     myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);   
46     return 0;  
47 }  

myprintf函数中也有接近的预订,明确参数表中最后3个参数是三个const char*
类型的字符串,在那一个字符串中冒出“%d”和“%x”次数的和正是简轻便单号参数表中参数的个数,省略号参数表中的参数类型也都以int类型。

一样的,实际的printf函数也有这么的预约:分明参数表中最终1个参数是三个const
char*
类型的字符串,省略号参数表中参数个数正是以此字符串中冒出的“%d”,“%x”,“%s”…次数的和,省略号参数表中参数的连串也是由“%d”,“%x”,“%s”……等格式化字符来提醒的。

之所以,类printf函数中省略号参数表中参数的个数和项目都以由类printf函数中的那个格式化字符串来支配的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注