自己动手开发调试器 01

背景:
    在做XXX编译器检证时经常需要区分是代码端错误,还是编译器端错误,因此对代码进行调试是必不可少的。但是狗日的甲方并没有提供对应的调试器XXXDB,而用GDB调试XXX生成的可执行程序很不稳定,经常出现异常,干脆自己动手,写mini调试器,顺便学习一下开发一个调试器到底需要哪些知识。

目标:
    GDB一共有十几万行代码,95%的功能都用不上。三个最基本的功能:“单步”、“断点”、“查看变量”即可满足日常工作中的大部分需求。并且基于学习、分享的初衷,我尽量把代码控制在千行左右,足够简单,足够傻瓜,最关键的是,老夫没那么时间啊。

预备知识:
    先简单解释下调试器的基本原理。
    假设调试器进程为A,被调试程序的进程为B. 如果要实现“单步”、“断点” 和“查看变量”三种基本功能,那也就意味着A进程必须要拥有三种操控B进程的能力:
    1   A可以暂停B进程的执行
    2   A可以恢复B进程的执行
    3   A可以在任意时刻查看B进程的内存及寄存器
    显然,所谓“断点”就是在某个特定“时刻”暂停B进程的执行;所谓“单步”就是先恢复B进程的执行“一小会儿”,然后立刻暂停;所谓watch变量,就是查看特定内存或者某个寄存器,不管啥变量都只能存在这俩地方。

    问题是,如果你是进程B,你不会觉得很不踏实么,居然有人可以这么样将你玩弄在鼓掌之中,你在他面前根本就是完全透明,毫无秘密,任人蹂躏。很显然,不应该有这么苦逼的事情发生。或者说,一个普通的用户进程不可能仅通过什么绚烂的编程技巧来做到这一点,再或者说,这必须是操作系统提供的“能力”。

    认识到这一点很重要,也就是说如果是linux,那就应该是某些神奇的系统调用,如果是windows,那就应该是某些拥有又臭又长参数的API,如果你的操作系统没提供这样的接口,那你就不要想了(仅限于二进制代码,基于虚拟机的,解释器的不算)。 windows下的不知道也暂时不关心,linux下的就是“ptrace”,32位/64位都是它。 因为第一篇文章嘛,只是简单解释下,而且后面要说的还有很多,所以我就不详细介绍了,关于ptrace的资料你可以参考

原版:

    http://www.linuxjournal.com/article/6100
    http://www.linuxjournal.com/node/6210/print
中文版:

  http://www.kgdb.info/gdb/playing_with_ptrace_part_i/

  http://www.kgdb.info/gdb/playing_with_ptrace_part_ii/


    但是有一个关键点需要仔细说明一下,进程A怎么通过ptrace让进程B暂停? 这么说吧,首先进程A通过ptrace可以改写B进程空间的任意地址的内容,当然也就能改写B进程的机器指令,比如下面的超白痴C代码

1 //test.c
2 int main()
3 {
4 return 0;
5 }


先编译 gcc test.c -o test,然后用objdump -d test 反汇编下

1  0000000000400474 <main>:
2 400474: 55 push %rbp
3 400475: 48 89 e5 mov %rsp,%rbp
4 400478: b8 00 00 00 00 mov $0x0,%eax
5 40047d: c9 leaveq
6 40047e: c3 retq
7 40047f: 90 nop



main函数一共6条指令,
第一条在 0x400474处,1个字节,内容是"0x55", 意思是  push   %rbp
第二条在 0x400475处,3个字节,内容是"0x48 0x89 0xe5", 意思是   mov %rsp,%rbp
...省略...


如果我想B进程在第3行暂停,或者说在第3行设置一个断点,那么在进程B运行到第3行之前,进程A通过ptrace修改进程B内存空间0x400478处, 将第一个字节(0xb8)修改成(0xcc),那么进程B运行到第三行自动就暂停了。为啥?因为0xcc就是INT 3 指令,先show一些官方文档吧:

==============================================
Opcode Instruction Description
CC      INT3           Interrupt 3—trap to debugger
CD ib  INT imm8    Interrupt vector numbered by immediate byte
CE      INTO           Interrupt 4—if overflow flag is 1

Intel® Itanium® Architecture Software Developer’s Manual
Volume 2: System Architecture
==============================================

==============================================
The INT 3 instruction generates a special one byte opcode (CC) that is
intended for calling the
debug exception handler. (This one byte form is valuable because it can
be used to replace the
first byte of any instruction with a breakpoint, including other one
byte instructions, without
over-writing other code).

Intel Architecture Software Developer’s Manual
Volume 2:Instruction Set Reference
================================================

看不懂没关系,原理很简单,0xcc就是“暂停”(Trap)指令,并且它只有一个字节。64位下的机器指令的长度不等,比如上面的6条指令就有1,3,5几种,但是最小必须是1,也就是说INT 3是最短的一条指令,那它就能覆盖到任意一条指令的最开始部分,比如,把它覆盖到0x400478处,

第4行 400478:       b8 00 00 00 00          mov    $0x0,%eax

就变成了

第4行  400478:       cc 00 00 00 00          mov    $0x0,%eax 

 

除了第一个“操作符”变了,其他的“操作数”都没变 ,当B进程执行到0x400478处时,它就会暂停,然后将控制权交给父进程,也就是A,然后A干完它想干的事情,比如查查寄存器,看看内存啥的,再把B的0x400478处改回来,于是又变成了

第4行  400478:       b8 00 00 00 00          mov    $0x0,%eax 

进程内存一点儿没变,但是这时候指令寄存器(SP? IP? 反正好几种叫法)已经指向下一条指令了,也就是b8后面的00,为啥?因为b8以前cc,单字节指令,执行过了,ip往前挪了一个字节,于是指向00了,所以A进程通过ptrace把指令寄存器-1,于是又指向了b8,一切如常,继续执行。

ok,总结一下。
假设你想设置几个断点,那么首先确定好位置,比如0x400474, 0x400478,0x40047e,然后流程如下:
================================================
a 保存位置的第一个字节,然后修改位置的第一个字节为0xcc(INT 3)
b 继续B进程
c B进程遇到断点暂停,将控制权交还A进程
d A进程将断点位置的第一个字节改回来,将指令寄存器-1,继续B进程,转入步骤b.
================================================


假设你想单步执行,在能设置断点基础上,流程如下:
================================================
a 将断点设在下一条指令处,继续B进程
b B进程遇到断点暂停,转入a步骤
================================================
瞧瞧,原来单步执行就是不停的在下一条指令前设断点啊...


后记:
    在上面的内容中,我屏蔽了很多细节,比如:

   1 “下一条指令”,假设你在0x400475处

第3行  400475:       48 89 e5                mov    %rsp,%rbp
第4行 400478: b8 00 00 00 00 mov $0x0,%ea

 
显然,下一条指令在0x400478处,也就是3个字节之后,问题是你怎么知道要去跳
过“3”个字节,为啥不是2个,不是1个?很显然因为0x400475指令的内容“48
89 e5”告诉你这条指令有3个字节长。它怎么告诉你的?“48 89 e5”这6个字母
里面一个“3”都没有。

  2  “B进程将控制权交还给A”,B怎么就还给A了?B与A到底通过什么样的方式
来交互?进程间交还还是线程间交互?

  3   到目前为止,操作的都是机器码,我能停在0x400475处有什么用?我需要的
是能停在 "int i = 0;"处。换句话说,如何建立机器码与源代码之间的关系。

实现:
    在参考文献的链接中,提供了关于ptrace的C代码示例。不过这种有历史的东西,肯定有一大堆封装好的库。这里我用的python的封装,python-ptrace。

    python-ptrace本身提供了一个gdb.py,800行左右代码。基本上局部了简单的单线程汇编代码调试能力。不过,我的目标是提供源代码级的调试功能,而且还要限制在千行左右,gdb就有点大了,自己简单写搭了个框架,200行,先实现了汇编码的单步执行,慢慢扩展。


当前要执行的汇编代码,效果如下:
================================================
In [6]: run fdb.py ../test/test
fdb: step
fdb: command:step params:[]
fdb: a_step
Assembly:  0x000000360ae00af0: MOV RDI, RSP
fdb: step
fdb: command:step params:[]
fdb: a_step
Assembly:  0x000000360ae00af3: CALL 0x360ae01120
fdb: step
fdb: command:step params:[]
fdb: a_step
Assembly:  0x000000360ae00af8: MOV R12, RAX
================================================

    具体源码在附件,但是首先,它依赖一些第三方库,其次它只支持64 位,linux,再次,它是python实现的,再次,我刚开了个头。

cd /usr/tmp/luqi/python-ptrace-0.6.3
fdb.py ../test/test
fdb: step


    后面我会继续解释上面的一些细节,进一步补充理论,也会深入到具体代码实现,作为一个开头,这次的内容已经很多,欢迎有这方面经验的兄弟一起交流,因为,其实我也有很多不明白的地方想要找高人请教。

参考文献:
    互联网上关于调试器的内容并不多,先贡献一个精品
    http://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1/

    http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints/

    http://eli.thegreenplace.net/2011/02/07/how-debuggers-work-part-3-debugging-information/

 

附件地址:

http://files.cnblogs.com/quixotic/fdb.rar

转载于:https://www.cnblogs.com/quixotic/archive/2012/01/16/2323541.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275057.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

02如何抓住重点,系统高效地学习数据结构与算法?

以下内容总结自极客时间王争大佬的《数据结构与算法之美》课程&#xff0c;本文章仅供个人学习总结。 什么是数据结构?什么是算法? 从广义上讲&#xff0c;数据结构就是指一组数据的存储结构。算法就是操作数据的一组方法。 类比图书馆的书籍&#xff0c;我们如果想找一本书可…

第2年,倒数第3天,1.5万票,感动!

1源码共读大家好&#xff0c;我是若川。众所周知。从8月份开始&#xff0c;我组织了源码共读活动&#xff0c;至今已经有5个月了&#xff0c;每周一期&#xff0c;进行到了第18期。每周坚持写源码解读文章&#xff0c;每天坚持答疑解惑&#xff0c;帮助了很多人学会看源码&…

启发式搜索给神经网络_神经科学如何支持UX启发式

启发式搜索给神经网络重点 (Top highlight)Interaction and UX designers have long known and used heuristics to guide the creation of a user-friendly interface. We know empirically that these principles work, and they make “common sense”. These heuristics th…

Django实战(1):需求分析和设计

Depot是《Agile Web Development with Rails》中的一个购物车应用。 该书中用多次迭代的方法&#xff0c;逐步实现购物车应用&#xff0c;使很多人走上了rails开发的道路。 遗憾的是Django世界中好像没有类似的指引&#xff0c;也许是因为pythoner 不需要具体的例子。 但是如果…

使用 apiDoc 为你的Node.js API 生成文档

翻译&#xff1a; 疯狂的技术宅 原文&#xff1a;jonathas.com/documenting… 未经许可&#xff0c;禁止转载&#xff01; 当你为其他开发人员&#xff08;前端&#xff0c;桌面&#xff0c;移动等&#xff09;开发 API 时&#xff0c;需要生成一份风格良好的文档&#xff0c;以…

海浪 shader_海浪下的发现

海浪 shaderI’ve been playing Subnautica for over 25 hours now, and likely have at least that many more to go. The game puts you in the shoes of a crew member on the Aurora, a spaceship that suffers a catastrophic incident and plummets to the largely ocean…

最后一天,特邀小姐姐配音拉票,今日可投28票

1源码共读大家好&#xff0c;我是若川。最后一天&#xff0c;特邀小姐姐配音拉票&#xff0c;超级好听。众所周知。从8月份开始&#xff0c;我组织了源码共读活动&#xff0c;至今已经有5个月了&#xff0c;每周一期&#xff0c;进行到了第18期。每周坚持写源码解读文章&#x…

NET中使用Memcached的相关资源整理

本文转自&#xff1a;http://www.cnblogs.com/dudu/archive/2009/07/19/1526407.html Memcached官方站点&#xff1a;http://www.danga.com/memcached / Memcached Win32 1.2.6下载&#xff1a;http://code.jellycan.com/memcached/ 安装帮助&#xff1a;Windows下的.NET Memca…

FFMPEG 视频图像解封装解码

FFMPEG4.0 音频解码解封装FFMPEG 音频封装编码 下面的函数方法基于最新的FFMPEG 4.0&#xff08;4.X&#xff09;&#xff1a;本文讲是如何从一个视频文件中提取出其中的图像数据&#xff0c;并将图像数据保存到文件中。 解码解封装的过程与音频差不多&#xff0c;具体如下&…

对数据可视化的理解_使数据可视化更容易理解

对数据可视化的理解Data is weaving its way into almost all aspects of our lives since the past decade. Our ability to store more information in smaller and smaller spaces has encouraged us to make sure we leave no information out. The ease of collecting inf…

面试官:项目中常用的 .env 文件原理是什么?如何实现?

1. 前言大家好&#xff0c;我是若川。持续组织了5个月源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。本文仓库 h…

语言分类,我接触和我想学习的

本文信息和数据出自hyperpolyglot&#xff0c;将当前主流编程语言分为11个大类&#xff0c;分别为&#xff1a;解释型(PHP,Perl,Python,Ruby,Tcl,Lua,JavaScript,Io)、操作系统自动化型(POSIX Shell,AppleScript,PowerShell)、C风格(C,Objective C,Java,C#)、Pascal风格(Pascal…

梯度下降法和随机梯度下降法

1. 梯度 在微积分里面&#xff0c;对多元函数的参数求∂偏导数&#xff0c;把求得的各个参数的偏导数以向量的形式写出来&#xff0c;就是梯度。比如函数f(x,y), 分别对x,y求偏导数&#xff0c;求得的梯度向量就是(∂f/∂x, ∂f/∂y)T,简称grad f(x,y)或者▽f(x,y)。对于在点(x…

一张图看程序媛阿源的2021个人年度流水账

大家好&#xff0c;我是若川。持续组织了5个月源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。本文来自读者阿源小…

案例研究:设计与方法_如何进行1小时的重新设计(案例研究)

案例研究:设计与方法速度设计简介 (Intro to Speed Designing) I’ve been an advocate of speed redesigning technique for a while. The idea is simple — decrease the hand-eye lag and make super quick decisions, seemingly without thinking. The logic behind it is…

图文并茂重新认识下递归

大家好&#xff0c;我是若川。持续组织了5个月源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。对于大部分前端(包…

《C和指针》读书笔记

看过了经典的K&R C&#xff0c;又看了这本Pointers on C&#xff0c;温习了C语言的基本语法。 在重温过程中&#xff0c;感觉需要重点把握的知识是指针、结构和动态内存分配。 这对今后的算法和操作系统方面的研究学习很有帮助。 3.2.3 声明指针int* b, c, d;本以为这条语句…

FPGA设计者的5项基本功

记得《佟林传》里&#xff0c;佟林练的基本功是“绕大树、解皮绳”&#xff0c;然后才练成了什么“鬼影随行、柳叶绵丝掌”。 在我看来&#xff0c;成为一名说得过去的FPGA设计者&#xff0c;需要练好5项基本功&#xff1a;仿真、综合、时序分析、调试、验证。 需要强调的一点是…

unity 全息交互ui_UI向3D投影全息界面的连续发展

unity 全息交互uiThe user interface has been natural in its evolution and strategically heading towards the 3D-projection holographic interface (3D-PHI) era.用户界面在其发展过程中一直很自然&#xff0c;并且在战略上正朝着3D投影全息界面( 3D-PHI )时代迈进。 Si…

开发工具 快捷键整理

快捷键大全 JAVA 开发工具 MyEclipse -------------------------------------MyEclipse 快捷键1(CTRL)-------------------------------------Ctrl1 快速修复CtrlD: 删除当前行 CtrlQ 定位到最后编辑的地方 CtrlL 定位在某行 CtrlO 快速显示 OutLine CtrlT 快速显示当前类…