int main()
{
printf("a:%d, b:%d\n");
char *chs= "hello";
int a = 0x1234;
printf("a:%d, s:%s\n", a, chs);
int b = 0x0188;
__asm__("mov $0x1111, %rdx");//这里,我们手动写入rdx 寄存器的值,是为了给第三个参数一个“非法”的垃圾值
printf("a:%d, s:%s\n", b);//crash: 因为这里会试图打印出,以0x1111为起始地址的字符串
return 0;
}对于可变参数函数,作为被调用方,是不会校验参数的合理性的。被调用方只会去对应的位置(寄存器 或者栈)寻找位置。因此一旦对应位置的垃圾数值不可访问,就会导致crash的发生。
3.4.4 内存越界内存越界是一个相对比较宽泛的概念。最典型的就是数组下标越界,看一个例子。#include<cstring>
#include<cstdio>
#include<string>
class Base {
public:
virtual void print() {
printf("print\n");
}
void member_function () {
printf("member\n");
}
};
int i = 0;
Base *foop = NULL;
void CopyData(char const *szData)
{
Base foo1;
foop = &foo1;
char cDest;
foop->print();
cDest = 'a';
foo1.member_function();
foo1.print();
foop->member_function();
foop->print();//crash here
}
int main()
{
CopyData("1234567890123456789012345678901234567890");
return 0;
}
/*
member
member
2335 segmentation fault (core dumped)
*/这里cDest = 'a';是对应数组下标越界,而这里的越界会导致foo1对象的虚函数表指针被写入垃圾数值,因而会导致CopyData函数的最后一句调用失败。由于栈上保存了很多信息,尤其是一些关键寄存器的信息:EIP和EBP等。这些缓存的寄存器被写入垃圾数值之后,可能导致函数栈回溯失败、非法指令等问题,甚至由此衍生出缓冲区溢出等攻击手段(现代操作系统已经几乎不存在)。一个常见的栈数据破坏性写入的场景是使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆.
3.5 栈破坏下crash的分析与调试方法对于crash的分析思路,我们都知道首先使用bt命令查看crash对应的函数调用栈。但是在一些场景下,线程本身的栈结构被破坏,无法通过bt查看函数调用栈的情况。那么如何在函数调用的开始和结束执行对应的操作呢?g++/gcc正好提供了这种功能,能够让我们在函数的开始和结束嵌入对应的代码。 具体的操作方法可以参见另外一篇文章《如何处理栈被破坏的crash》
4.背景知识4.1 进程地址空间布局进程的地址空间布局(下图为32b系统)。
这个不够直观,我们来看一个具体的程序://memory.cpp
#include<stdlib.h>
#include<iostream>
#include<stdio.h>
int a = {0};
int b;
static char* global_static_inited = "static_data";
static char* global_static_uninited;
int main()
{
int tmp;
void *heap_small = malloc(10);
void *heap_start = malloc(4*1024*1024 + 30);
void *heap_second = malloc(4*1024*1024);
printf("a:%p, \nb:%p, \nstatic_data:%p, \nglobal_static_uninited:%p, \ntmp:%p, \nhead_small:%p, \nheap_start:%p, \nheap_second:%p\n",\
a, b, global_static_inited, &global_static_uninited, tmp, heap_small, heap_start, heap_second);
while(1);
return 0;
}
/*
a:0x601080,
b:0x601220,
static_data:0x4008e0,
global_static_uninited:0x601548,
tmp:0x7fffb3267ef0,
head_small:0x1e64040,
heap_start:0x7f65e8ab2010,
heap_second:0x7f65e86b1010
*/启动程序,运行pmap -d $thread_idAddress Kbytes ModeOffset Device Mapping
0000000000400000 4 r-x-- 0000000000000000 0fd:00017 a.out
0000000000600000 4 r---- 0000000000000000 0fd:00017 a.out
0000000000601000 4 rw--- 0000000000001000 0fd:00017 a.out
0000000001e64000 132 rw--- 0000000000000000 000:00000 [ anon ]
00007f65e86b1000 8200 rw--- 0000000000000000 000:00000 [ anon ]
00007f65e8eb3000 88 r-x-- 0000000000000000 0fd:00017 libpthread-2.17.so
00007f65e8ec9000 2048 ----- 0000000000016000 0fd:00017 libpthread-2.17.so
00007f65e90c9000 4 r---- 0000000000016000 0fd:00017 libpthread-2.17.so
00007f65e90ca000 4 rw--- 0000000000017000 0fd:00017 libpthread-2.17.so
00007f65e90cb000 16 rw--- 0000000000000000 000:00000 [ anon ]
00007f65e90cf000 12 r-x-- 0000000000000000 0fd:00017 libdl-2.17.so
00007f65e90d2000 2044 ----- 0000000000003000 0fd:00017 libdl-2.17.so
00007f65e92d1000 4 r---- 0000000000002000 0fd:00017 libdl-2.17.so
00007f65e92d2000 4 rw--- 0000000000003000 0fd:00017 libdl-2.17.so
00007f65e92d3000 1752 r-x-- 0000000000000000 0fd:00017 libc-2.17.so
00007f65e9489000 2048 ----- 00000000001b6000 0fd:00017 libc-2.17.so
00007f65e9689000 16 r---- 00000000001b6000 0fd:00017 libc-2.17.so
00007f65e968d000 8 rw--- 00000000001ba000 0fd:00017 libc-2.17.so
00007f65e968f000 20 rw--- 0000000000000000 000:00000 [ anon ]
00007f65e9694000 84 r-x-- 0000000000000000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
00007f65e96a9000 2044 ----- 0000000000015000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
00007f65e98a8000 4 r---- 0000000000014000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
00007f65e98a9000 4 rw--- 0000000000015000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
00007f65e98aa000 1028 r-x-- 0000000000000000 0fd:00017 libm-2.17.so
00007f65e99ab000 2044 ----- 0000000000101000 0fd:00017 libm-2.17.so
00007f65e9baa000 4 r---- 0000000000100000 0fd:00017 libm-2.17.so
00007f65e9bab000 4 rw--- 0000000000101000 0fd:00017 libm-2.17.so
00007f65e9bac000 932 r-x-- 0000000000000000 0fd:00017 libstdc++.so.6.0.19
00007f65e9c95000 2048 ----- 00000000000e9000 0fd:00017 libstdc++.so.6.0.19
00007f65e9e95000 32 r---- 00000000000e9000 0fd:00017 libstdc++.so.6.0.19
00007f65e9e9d000 8 rw--- 00000000000f1000 0fd:00017 libstdc++.so.6.0.19
00007f65e9e9f000 84 rw--- 0000000000000000 000:00000 [ anon ]
00007f65e9eb4000 24 r-x-- 0000000000000000 0fd:00017 gundam_preload.so
00007f65e9eba000 2044 ----- 0000000000006000 0fd:00017 gundam_preload.so
00007f65ea0b9000 4 r---- 0000000000005000 0fd:00017 gundam_preload.so
00007f65ea0ba000 4 rw--- 0000000000006000 0fd:00017 gundam_preload.so
00007f65ea0bb000 132 r-x-- 0000000000000000 0fd:00017 ld-2.17.so
00007f65ea2b3000 28 rw--- 0000000000000000 000:00000 [ anon ]
00007f65ea2da000 8 rw--- 0000000000000000 000:00000 [ anon ]
00007f65ea2dc000 4 r---- 0000000000021000 0fd:00017 ld-2.17.so
00007f65ea2dd000 4 rw--- 0000000000022000 0fd:00017 ld-2.17.so
00007f65ea2de000 4 rw--- 0000000000000000 000:00000 [ anon ]
00007fffb3267000 4104 rw--- 0000000000000000 000:00000 [ stack ]
00007fffb372f000 8 r-x-- 0000000000000000 000:00000 [ anon ]
ffffffffff600000 4 r-x-- 0000000000000000 000:00000 [ anon ]
mapped: 31104K writeable/private: 12640K shared: 0K我们逐一解释:
[*]代码段
上文中0000000000400000是段的开始地址,并不是代码的开始地址。这个位置本身是不可访问的。代码段本身的权限是r + x. 需要注意的是static_data 这个字符串本身,是在代码段中。
(gdb) x/100i 0x000000000400000
0x400000: Cannot access memory at address 0x400000
(gdb) disassemble main
Dump of assembler code for function main():
0x0000000000400730 <+0>: push %rbp
0x0000000000400731 <+1>: mov %rsp,%rbp
0x0000000000400734 <+4>: sub $0x400010,%rsp
0x000000000040073b <+11>: mov $0x40001e,%edi
0x0000000000400740 <+16>: callq0x400600 <malloc@plt>
0x0000000000400745 <+21>: mov %rax,-0x8(%rbp)
0x0000000000400749 <+25>: mov $0x400000,%edi
0x000000000040074e <+30>: callq0x400600 <malloc@plt>
0x0000000000400753 <+35>: mov %rax,-0x8(%rbp)
0x0000000000400757 <+39>: jmp 0x400757 <main()+39>
End of assembler dump.
(gdb) x/20i 0x0000000000400730
0x400730 <main()>: push %rbp
0x400731 <main()+1>: mov %rsp,%rbp
0x400734 <main()+4>: sub $0x400010,%rsp
0x40073b <main()+11>: mov $0x40001e,%edi
0x400740 <main()+16>: callq0x400600 <malloc@plt>
0x400745 <main()+21>: mov %rax,-0x8(%rbp)
0x400749 <main()+25>: mov $0x400000,%edi
0x40074e <main()+30>: callq0x400600 <malloc@plt>
0x400753 <main()+35>: mov %rax,-0x8(%rbp)
0x400757 <main()+39>: jmp 0x400757 <main()+39>
[*]数据段
存放全局变量,包括静态和非静态,可以看到初始化和为初始化的数据,在同一个段上。这个段中的变量包括:a、b、global_static_uninited。
[*]函数局部变量
对应tmp,对应stack区域, 权限是rw
[*]堆空间
广义上,我们把mmap对应的区间也算作堆空间。这部分对应heap_start和heap_second. 狭义的堆空间,是heap_small对应的空间,起始段地址远小于heap_start对应的空间。
页:
1
[2]