调试器是个大骗子

科技资讯 投稿 7600 0 评论

调试器是个大骗子

作为一个调试器,调试分析是我的看家本领,像是给目标进程设置断点,或者让它单步执行,又或是查看进程中的变量、内存数据、CPU的寄存等等操作,我都手到擒来。

我之所以有这些本事,都得归功于一个强大的系统函数,它的名字叫ptrace。

它的第一个参数是一个枚举型的变量,表示要执行的操作,我支持的调试命令很多都是靠它来实现的:

不过在执行你的程序之前,我会在子进程中调用ptrace函数,然后指定第一个参数为PTRACE_TRACEME,这样一来,我就能监控子进程中发生的事情了,也才能对你指定的程序进行调试。

软件断点
作为一个调试器,最常用的功能就是给程序下断点了。

当我收到你的命令之后,我会偷偷把被调试进程中那个位置的指令修改为一个0xCC,这是一条特殊指令的CPU机器码——int 3,是x86架构CPU专门用来支持调试的指令。

一旦被调试的进程运行到那个位置,CPU执行这条特殊的指令时,会陷入内核态,然后取出中断描述符表IDT中的3号表项中的处理函数来执行。

在没有下一步指示之前,被调试的进程都不会进入就绪队列被调度执行。

这就是我给程序下断点的秘密。

我有一个非常巧妙的办法,就是让它单步执行,只执行一条指令,然后又会中断到我这里,但这时候我并不会通知程序员,而仅仅是把刚才恢复的断点又给打上(替换指令),然后就继续运行。这一切都发生的神不知鬼不觉,程序员根本察觉不到。

说到单步执行,应该算是程序员调试程序的时候除了下断点之外最常见的操作了,每一次只让被调试的进程运行一条指令,这样方便跟踪排查问题。

单步执行的实现可比下断点简单多了,我不用去修改被调试进程内存中的指令,只需要调用ptrace函数,传递一个PTRACE_SINGLESTEP参数就行了,操作系统会自动把它设置为单步执行的模式。

原来x86架构CPU有一个标志寄存器,名叫eflags,它里面不止包含了程序运行的一些状态,还有一些工作模式的设定。

接下来的事情就跟命中断点差不多了,我会截获到内核发给被调试进程的SIGTRAP信号,然后等待程序员的下一步指令。

如果你有程序的源代码,你还可以进行源码级别的单步调试,不过这里的单步就指的是源代码中的一行了。

内存断点
有的时候,直接给程序中代码的位置下断点并不能包治百病。比如程序员发现某个内存地址的内容老是莫名其妙被修改,想知道到底是哪个函数干的,这时候连地址都没有,根本没法下断点。

不用担心,我可以帮你解决这个烦恼。

猜猜我是如何做到的呢?

不过这种方式实在是太麻烦了,会严重拖垮被调试进程的性能。

在x86架构CPU的内部内置了一组调试寄存器,从DR0到DR7,总共8个。通过在DR0-DR3中设置要监控的内存地址,然后在DR7中设置要监控的模式,是读还是写,剩下的交给CPU就好了。

CPU内部依靠硬件电路来完成监控,可比我们软件一条一条的检查快多了!

我叫GDB,是你调试程序的好伙伴,现在你该知道我是如何工作的了吧!

这里是编程技术宇宙,一个专注用故事分享硬核又有趣计算机知识的公众号~

觉得不错的话,欢迎一键三连哦~

编程笔记 » 调试器是个大骗子

赞同 (31) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽