Crash/coredump 原理与实例

[复制链接]
 楼主| keer_zu 发表于 2023-2-7 17:09 | 显示全部楼层
3.4.3 可变参数函数参数个数不符合预期
  1. #include<cstdio>
  2. int main()
  3. {
  4.     printf("a:%d, b:%d\n");
  5.     char *chs= "hello";
  6.     int a = 0x1234;
  7.     printf("a:%d, s:%s\n", a, chs);
  8.     int b = 0x0188;
  9.     __asm__("mov $0x1111, %rdx");//这里,我们手动写入rdx 寄存器的值,是为了给第三个参数一个“非法”的垃圾值
  10.     printf("a:%d, s:%s\n", b);//crash: 因为这里会试图打印出,以0x1111为起始地址的字符串
  11.     return 0;
  12. }
对于可变参数函数,作为被调用方,是不会校验参数的合理性的。被调用方只会去对应的位置(寄存器 或者栈)寻找位置。因此一旦对应位置的垃圾数值不可访问,就会导致crash的发生。

 楼主| keer_zu 发表于 2023-2-7 17:09 | 显示全部楼层
3.4.4 内存越界
内存越界是一个相对比较宽泛的概念。最典型的就是数组下标越界,看一个例子。
  1. #include<cstring>
  2. #include<cstdio>
  3. #include<string>
  4. class Base {
  5.     public:
  6.     virtual void print() {
  7.         printf("print\n");
  8.     }
  9.     void member_function () {
  10.         printf("member\n");
  11.     }
  12. };

  13. int i = 0;
  14. Base *foop = NULL;
  15. void CopyData(char const *szData)
  16. {
  17.     Base foo1;
  18.     foop = &foo1;
  19.     char cDest[1];
  20.     foop->print();
  21.     cDest[8] = 'a';
  22.     foo1.member_function();
  23.     foo1.print();
  24.     foop->member_function();
  25.     foop->print();//crash here
  26. }
  27. int main()
  28. {
  29.     CopyData("1234567890123456789012345678901234567890");
  30.     return 0;
  31. }
  32. /*
  33. print
  34. member
  35. print
  36. member
  37. [1]    2335 segmentation fault (core dumped)
  38. */
这里cDest[8] = 'a';是对应数组下标越界,而这里的越界会导致foo1对象的虚函数表指针被写入垃圾数值,因而会导致CopyData函数的最后一句调用失败。
由于栈上保存了很多信息,尤其是一些关键寄存器的信息:EIP和EBP等。这些缓存的寄存器被写入垃圾数值之后,可能导致函数栈回溯失败、非法指令等问题,甚至由此衍生出缓冲区溢出等攻击手段(现代操作系统已经几乎不存在)。一个常见的栈数据破坏性写入的场景是使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆.

 楼主| keer_zu 发表于 2023-2-7 17:10 | 显示全部楼层
3.5 栈破坏下crash的分析与调试方法
对于crash的分析思路,我们都知道首先使用bt命令查看crash对应的函数调用栈。但是在一些场景下,线程本身的栈结构被破坏,无法通过bt查看函数调用栈的情况。那么如何在函数调用的开始和结束执行对应的操作呢?g++/gcc正好提供了这种功能,能够让我们在函数的开始和结束嵌入对应的代码。 具体的操作方法可以参见另外一篇文章《如何处理栈被破坏的crash》

 楼主| keer_zu 发表于 2023-2-7 17:11 | 显示全部楼层
4.背景知识4.1 进程地址空间布局
进程的地址空间布局(下图为32b系统)。
638463e2158957170.png

这个不够直观,我们来看一个具体的程序:
  1. //memory.cpp
  2. #include<stdlib.h>
  3. #include<iostream>
  4. #include<stdio.h>
  5. int a[100] = {0};
  6. int b[200];
  7. static char* global_static_inited = "static_data";
  8. static char* global_static_uninited;
  9. int main()
  10. {
  11.     int tmp[1024*1024];
  12.     void *heap_small = malloc(10);
  13.     void *heap_start = malloc(4*1024*1024 + 30);
  14.     void *heap_second = malloc(4*1024*1024);
  15.     printf("a:%p, \nb:%p, \nstatic_data:%p, \nglobal_static_uninited:%p, \ntmp:%p, \nhead_small:%p, \nheap_start:%p, \nheap_second:%p\n",\
  16.             a,    b,    global_static_inited,    &global_static_uninited,   tmp,     heap_small,      heap_start,    heap_second);
  17.     while(1);
  18.     return 0;
  19. }
  20. /*
  21. a:0x601080,
  22. b:0x601220,
  23. static_data:0x4008e0,
  24. global_static_uninited:0x601548,
  25. tmp:0x7fffb3267ef0,
  26. head_small:0x1e64040,
  27. heap_start:0x7f65e8ab2010,
  28. heap_second:0x7f65e86b1010
  29. */
启动程序,运行pmap -d $thread_id
  1. Address           Kbytes Mode  Offset           Device    Mapping
  2. 0000000000400000       4 r-x-- 0000000000000000 0fd:00017 a.out
  3. 0000000000600000       4 r---- 0000000000000000 0fd:00017 a.out
  4. 0000000000601000       4 rw--- 0000000000001000 0fd:00017 a.out
  5. 0000000001e64000     132 rw--- 0000000000000000 000:00000   [ anon ]
  6. 00007f65e86b1000    8200 rw--- 0000000000000000 000:00000   [ anon ]
  7. 00007f65e8eb3000      88 r-x-- 0000000000000000 0fd:00017 libpthread-2.17.so
  8. 00007f65e8ec9000    2048 ----- 0000000000016000 0fd:00017 libpthread-2.17.so
  9. 00007f65e90c9000       4 r---- 0000000000016000 0fd:00017 libpthread-2.17.so
  10. 00007f65e90ca000       4 rw--- 0000000000017000 0fd:00017 libpthread-2.17.so
  11. 00007f65e90cb000      16 rw--- 0000000000000000 000:00000   [ anon ]
  12. 00007f65e90cf000      12 r-x-- 0000000000000000 0fd:00017 libdl-2.17.so
  13. 00007f65e90d2000    2044 ----- 0000000000003000 0fd:00017 libdl-2.17.so
  14. 00007f65e92d1000       4 r---- 0000000000002000 0fd:00017 libdl-2.17.so
  15. 00007f65e92d2000       4 rw--- 0000000000003000 0fd:00017 libdl-2.17.so
  16. 00007f65e92d3000    1752 r-x-- 0000000000000000 0fd:00017 libc-2.17.so
  17. 00007f65e9489000    2048 ----- 00000000001b6000 0fd:00017 libc-2.17.so
  18. 00007f65e9689000      16 r---- 00000000001b6000 0fd:00017 libc-2.17.so
  19. 00007f65e968d000       8 rw--- 00000000001ba000 0fd:00017 libc-2.17.so
  20. 00007f65e968f000      20 rw--- 0000000000000000 000:00000   [ anon ]
  21. 00007f65e9694000      84 r-x-- 0000000000000000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
  22. 00007f65e96a9000    2044 ----- 0000000000015000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
  23. 00007f65e98a8000       4 r---- 0000000000014000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
  24. 00007f65e98a9000       4 rw--- 0000000000015000 0fd:00017 libgcc_s-4.8.5-20150702.so.1
  25. 00007f65e98aa000    1028 r-x-- 0000000000000000 0fd:00017 libm-2.17.so
  26. 00007f65e99ab000    2044 ----- 0000000000101000 0fd:00017 libm-2.17.so
  27. 00007f65e9baa000       4 r---- 0000000000100000 0fd:00017 libm-2.17.so
  28. 00007f65e9bab000       4 rw--- 0000000000101000 0fd:00017 libm-2.17.so
  29. 00007f65e9bac000     932 r-x-- 0000000000000000 0fd:00017 libstdc++.so.6.0.19
  30. 00007f65e9c95000    2048 ----- 00000000000e9000 0fd:00017 libstdc++.so.6.0.19
  31. 00007f65e9e95000      32 r---- 00000000000e9000 0fd:00017 libstdc++.so.6.0.19
  32. 00007f65e9e9d000       8 rw--- 00000000000f1000 0fd:00017 libstdc++.so.6.0.19
  33. 00007f65e9e9f000      84 rw--- 0000000000000000 000:00000   [ anon ]
  34. 00007f65e9eb4000      24 r-x-- 0000000000000000 0fd:00017 gundam_preload.so
  35. 00007f65e9eba000    2044 ----- 0000000000006000 0fd:00017 gundam_preload.so
  36. 00007f65ea0b9000       4 r---- 0000000000005000 0fd:00017 gundam_preload.so
  37. 00007f65ea0ba000       4 rw--- 0000000000006000 0fd:00017 gundam_preload.so
  38. 00007f65ea0bb000     132 r-x-- 0000000000000000 0fd:00017 ld-2.17.so
  39. 00007f65ea2b3000      28 rw--- 0000000000000000 000:00000   [ anon ]
  40. 00007f65ea2da000       8 rw--- 0000000000000000 000:00000   [ anon ]
  41. 00007f65ea2dc000       4 r---- 0000000000021000 0fd:00017 ld-2.17.so
  42. 00007f65ea2dd000       4 rw--- 0000000000022000 0fd:00017 ld-2.17.so
  43. 00007f65ea2de000       4 rw--- 0000000000000000 000:00000   [ anon ]
  44. 00007fffb3267000    4104 rw--- 0000000000000000 000:00000   [ stack ]
  45. 00007fffb372f000       8 r-x-- 0000000000000000 000:00000   [ anon ]
  46. ffffffffff600000       4 r-x-- 0000000000000000 000:00000   [ anon ]
  47. mapped: 31104K    writeable/private: 12640K    shared: 0K
我们逐一解释:
  • 代码段
上文中0000000000400000是段的开始地址,并不是代码的开始地址。这个位置本身是不可访问的。代码段本身的权限是r + x. 需要注意的是static_data 这个字符串本身,是在代码段中。

  1. (gdb) x/100i 0x000000000400000
  2.    0x400000:    Cannot access memory at address 0x400000
  3. (gdb) disassemble main
  4. Dump of assembler code for function main():
  5.    0x0000000000400730 <+0>:     push   %rbp
  6.    0x0000000000400731 <+1>:     mov    %rsp,%rbp
  7.    0x0000000000400734 <+4>:     sub    $0x400010,%rsp
  8.    0x000000000040073b <+11>:    mov    $0x40001e,%edi
  9.    0x0000000000400740 <+16>:    callq  0x400600 <malloc@plt>
  10.    0x0000000000400745 <+21>:    mov    %rax,-0x8(%rbp)
  11.    0x0000000000400749 <+25>:    mov    $0x400000,%edi
  12.    0x000000000040074e <+30>:    callq  0x400600 <malloc@plt>
  13.    0x0000000000400753 <+35>:    mov    %rax,-0x8(%rbp)
  14.    0x0000000000400757 <+39>:    jmp    0x400757 <main()+39>
  15. End of assembler dump.
  16. (gdb) x/20i 0x0000000000400730
  17.    0x400730 <main()>:   push   %rbp
  18.    0x400731 <main()+1>: mov    %rsp,%rbp
  19.    0x400734 <main()+4>: sub    $0x400010,%rsp
  20.    0x40073b <main()+11>:        mov    $0x40001e,%edi
  21.    0x400740 <main()+16>:        callq  0x400600 <malloc@plt>
  22.    0x400745 <main()+21>:        mov    %rax,-0x8(%rbp)
  23.    0x400749 <main()+25>:        mov    $0x400000,%edi
  24.    0x40074e <main()+30>:        callq  0x400600 <malloc@plt>
  25.    0x400753 <main()+35>:        mov    %rax,-0x8(%rbp)
  26.    0x400757 <main()+39>:        jmp    0x400757 <main()+39>


 楼主| keer_zu 发表于 2023-2-7 17:12 | 显示全部楼层
  • 数据段
存放全局变量,包括静态和非静态,可以看到初始化和为初始化的数据,在同一个段上。这个段中的变量包括:a、b、global_static_uninited。
  • 函数局部变量
对应tmp,对应stack区域, 权限是rw
  • 堆空间
广义上,我们把mmap对应的区间也算作堆空间。这部分对应heap_start和heap_second. 狭义的堆空间,是heap_small对应的空间,起始段地址远小于heap_start对应的空间。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部
快速回复 在线客服 返回列表 返回顶部