《DEBUG HACKS》中文版笔记(二)
来源:互联网 发布:精通c语言薪水有多少 编辑:程序博客网 时间:2024/06/07 02:53
HACK9 调试时必须的栈知识
下面是学习本章节的示例代码
#include <stdio.h>#include <stdlib.h>#define MAX 1024typedef unsigned long long u64;typedef unsigned int u32;u32 max_addend= MAX;u64 sum_till_MAX(u32 n){ u64 sum; n++; sum=n; if(n<max_addend) sum+=sum_till_MAX(n); return sum;}int main(int argc,char **argv){ u64 sum = 0; if(argc == 2) max_addend = atoi(argv[1]); if(max_addend > MAX ||max_addend == 0){ fprintf(stderr,"Invalid\n"); return 1; } sum = sum_till_MAX(0); printf("sum(0..%lu) =%llu\n",max_addend,sum); return 0;}
使用GDB操作栈帧
假设GDB中进程停止在以下状态
用rame命令查看现在选择的栈帧
现在选择的帧为0,可以查看该帧内的自动变量sum
选择上一层栈帧,可以查看变量
用up 和 down可以快捷的切换栈帧指向
用带Info命令的frame命令可以看到更详细的栈帧信息
HACK10 函数调用时的参数传递方法(x86_64)篇
程序异常结束、与预期行为不一致,这是十分常见的故障。有错误信息的话,只需进行字符串查找就能确定显示该信息的源代码位置,相对比较容易。但是,故障的真正原因有可能在显示错误信息之前很远的地方。例如,某个函数计算处错误的值,以该值为参数调用其他的函数。这种情况下,找出程序出错的位置
第三章 内核调试的准备
HACK 15 Oops信息的解读方法
Oops信息是内核中发生致命错误时输出的内核信息。信息中大致包含了错误概况、加载的模块、寄存器信息、栈跟踪信息等。不同架构和内核版本的显示稍有不同,但大体是一样的。
HACK 27 backtrace无法正确显示
栈破坏有时会导致问题难以分析。调试器的backtrace并非万能钥匙。
HACK 28数组非法访问导致内存破坏
可怀疑是缓冲区溢出的情况
可怀疑是缓冲区溢出的情况之一就是,即使指定了编译选项-g,利用GDB读入core并显示backtrace之后,栈帧中还是没有显示符号名,如下所示。
(gdb)bt
/#0 0x20565c62 in ?? ()
/#1 0x54648731 in ?? ()
/#2 0x23415785 in ?? ()
代码突然跳转到了或者调用者调用了错误的地址0x20565c62,导致了segmentation fault的发生。
(gdb) x/i 0x20565c62
0x20565c62: Cannot access memory at address 0x20565c62
运行地址的改变
那么,是从哪里跳转到和调用了不存在的地址呢?先来整理一下改变程序运行地址的方法。方法基本分为三类。
1. 第一类就是直接指定地址并调用,C语言中if或for语句等进行条件判断时会用到这种方法,调用同一源代码内的函数时也会使用这种方法。
2. 第二类就是指定一块内存区域,其中保存了跳转地址。
3. 第三类方法就是执行ret命令,用于函数结束时返回调用者函数。
如果bug破坏了这些方法用到的值(被错误的地址覆盖),就可能跳转到错误的地址。但是,第一类方法使用的地址很难被破坏,因为地址一般保存在只读空间内。所以,尝试破坏该地址就会产生segmentation fault。此时,core文件中会记录这一瞬间程序计数器的值,因此比较容易分析。
第二类第三类方法使用的地址位于GlobalOffsetTable或栈等可写的空间中,因此即使bug破坏其内容,也无法被检测到。运行时以segmentation fault的形式显露出来。
确定破坏跳转地址值的位置(栈破坏)
已开头栈跟踪为例,我们要寻找错误地写入0x20565c62这个数据的地方。
这种调查中,很重要的是需要怀疑数据是否为字符串的一部分,因为错误地将数据写入地址的典型情况之一就是字符串复制。由于字符串的输入长度很难预测,若缓冲区过小,再加上对输入字符串的长度检查不完善,就可能发生这种情况。
#include <stdio.h>#include <stdlib.h>char names[]="book cat dog building vegetable curry";void func(void){ char buf[5]; strcpy(buf,names);}int main(void){ func(); return 0;}
HACK30 malloc()和free()发生故障
错误使用内存相关库函数引起的bug
应用程序,特别是C语言应用程序的bug中,最常见的就是内存相关库函数的错误使用引发的bug,如内存的双重释放,访问分配空间之外的内存等bug。
问题似乎出在执行内存释放的库函数free()中,但实际上是使用free()函数的应用程序的问题,而不是库函数free()的问题。
最危险的情况是:程序可能不会受到内存破坏的影响而继续运行。此时可能会产生以下结果。
会在完全没关系的地方产生SIGSEGV。
使用被破坏的数据进行计算,产生错误的结果。
HACK31应用程序停止响应(死锁篇)
示例程序
#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int cnt = 0;void cnt_reset(void){ pthread_mutex_lock(&mutex); cnt = 0; pthread_mutex_unlock(&mutex);}void *thr(void *arg){ while(1){ pthread_mutex_lock(&mutex); if(cnt > 2) cnt_reset(); else cnt++; pthread_mutex_unlock(&mutex); sleep(1); }}int main (void){ pthread_t tid; pthread_create(&tid,NULL,thr,NULL); pthread_join(tid,NULL); return 0;}
ps的结果状态显示S可以怀疑是陷入了死锁。
-L选项可以显示所有线程。
接着用GDB attach到这个进程上,调查哪里在睡眠j
启动之后只输入bt命令,显示的并不是运行停止的那个线程,而是最初进入的线程backtrace
进入另一个线程查看情况
HACK43 使用strace寻找故障原因的线索
#include <stdio.h>#include <stdlib.h>int main(void){ FILE *fp; fp = fopen("/eea/dasdf","r"); return 0;}
attach到进程上
示例程序#include <stdio.h>#include <stdlib.h>int main(void){ while(1){ FILE *fp; fp = fopen("/eta/taet","r"); if(fp == NULL) printf("error\n"); else fclose(fp); sleep(3); } return 0;}
- 《DEBUG HACKS》中文版笔记(二)
- 《DEBUG HACKS》中文版笔记(一)
- 【J2ME】 Debug 笔记(二)
- 读书笔记 -- 《debug hacks中文版—深入调试的技术和工具》
- debug hacks源码
- C++ Primer 中文版 学习笔记(二)
- [Debug]Kernel panic学习笔记(二)
- DEBUG笔记二
- 《Debug Hacks》和调试技巧
- Swing Hacks 学习笔记
- Debug Hacks 深入调试的技术和工具(一) 热身准备
- 机器学习实战 笔记 debug kNN(二)
- Python debug(二)
- matlab debug(二)
- ffmpeg的tutorial中文版学习笔记(二)
- ffmpeg的tutorial中文版学习笔记(二)
- TensorFlow官方文档中文版-笔记(二)
- Servlet Api 中文版(二)
- 单片机程序风格和调试技巧(一)
- 关于两个变量值互换问题
- 设计模式(1)------工厂方法模式
- 剑指offer—打印1到最大n位数
- @class
- 《DEBUG HACKS》中文版笔记(二)
- shell脚本下常用的符号组合及转移字符
- HDOJ 1573 X问题 (余数不互质的中国剩余定理)
- android学习笔记(四)__xml和widget
- c语言学习笔记8之if语句
- 计算机网络 ip 子网掩码,子网数学习心得
- 啦啦啦啦~Django1.96---字段查询
- 70-Multiply Strings
- 搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群