这是开头
事情起于19级某份代码(代码的主人说求放过,所以这里就不贴出来了)出现了一个字符串的问题,这个问题本身也比较有意思,所以就有了这篇水贴。
这是正文
先给个样例:
/* try with: gcc -m32 -Og -g ./test.c */
#include <stdlib.h>
#include <stdio.h>
char* str1 = "Hello1";
char str2[] = "Hello2";
int main(){
char* str3 = "Hello3";
char str4[] = "Hello4";
char* str5 = (char*)malloc(sizeof(char)*8);
for(int i=0;i<7;i++) str5[i] = str4[i];
str5[5] = '5';
printf("%s\n",str1);
printf("%s\n",str2);
printf("%s\n",str3);
printf("%s\n",str4);
printf("%s\n",str5);
return 0;
}
上面代码只是用五种不同的方式创建了五个不同的字符串,打印出来的结果看上去没有什么差异,但其实这几个字符串是放在不同的地方的,而且有不同的特性(多图预警,特别留意地址)。
首先str1和str3(指向的东西)算是同一类型的,都是拿个指针指向一个字符串常量,这里常量的意思是这个字符串是不可被改变的,原因是它们被放在了.rodata(read only data)段,这个位置的东西都是只读不可写的。要注意的是只是str1和str3指向的字符串是放在.rodata,str1和str3这两个指针本身还是放在不同地方的。
str2算是第二种类型——全局变量,和上面的不同,它是可写的,因为它被放在了.data段(另:带初值的全局变量都会被放在这个段,没初值的全局变量会被放在一个叫.bss的段,而且初值被设为0)。
上图可以看到,.data段里除了str2这个字符串外,还存这str1这个指针,因为str1也是全局变量,下图的".WUV"就是str1指向的地址(little-endian)。
另给一下str3的图,可以看到"Hello1"和"Hello3"是相邻的(只是这份代码是相邻的,其他我不敢保证)。
str4和str2类似,放的地方不同,从地址就可以看出来距离隔了很远,str4被放在了一个叫栈(stack)的地方,栈的话其实可以吹很多东西,简单来说就是局部变量(local variable)会被放在这里。
最后的str5从代码上都可以看出它是异类了,特点是str5指向的地方是用malloc分配的,只要分配的大小不是非非非常大,malloc分配的空间都在堆(heap)上,从图来看会觉得str5的地址跟str1的差不多,但其实只是heap被放在了.rodata隔壁而已。
总结
ctf新生赛快开始了,欢迎报名~ !
(虽然我上个月就是这么说的。。。)