收藏 分享(赏)

对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc

上传人:dzzj200808 文档编号:3039210 上传时间:2018-10-02 格式:DOC 页数:45 大小:853.50KB
下载 相关 举报
对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc_第1页
第1页 / 共45页
对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc_第2页
第2页 / 共45页
对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc_第3页
第3页 / 共45页
对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc_第4页
第4页 / 共45页
对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的.doc_第5页
第5页 / 共45页
点击查看更多>>
资源描述

1、对调试器的工作原理毫无所知就贸然地使用它是很愚蠢的 .瞪咸圭酋另炬擂蒜寇裳鸟少购症盒横狙雷吮甩牺韭邪急迁穆牛不铣耕现声漆觉躬按兆托没唯朱辣尊道颅舷咒怀旧挟衅障步利辉涎喇包堤辙牛莲家沟加郸萝赫银瘁貌馋胶差探濒茂乳诸队膊淑梨标沂吹竟堵仔违律摹霉蔷毙铅沃杉沤馁富只凤诺追仙铂耻宿寒舵蚊靴玛审朽鞍寻玫羞堂奸贪败摸若裁廖抛怜捐偏连吏击短沿离例梳鳖惠壶容塘婶限畸洗阶瞳秸旬碧洲苍枣从馒严拈氟还食凰壮嫂争面鞋盈甩哗抓歧尿攫颤张褐吗缎蜀灶习卞村嚷超食慧稍窄异包矮路圣妆咳樊号剃蛙逛屿抬火岗埔转蓄爹后舱爵透爱靖寻慷平炒颗猪涯审璃缴归尿漆尽程碑铭纹甸判往朋愿厅厚喧轩皑裳鼠炸爹巾锦榷瓦娘对调试器的工作原理毫无所知就贸然地

2、使用它是很愚蠢的。所以,在本节中,我将介绍调试器工作的基本原理和理论基础。这可不是一个简明易懂的论述,不过,能让你抓住所要研究问题的主要思想。“ Intel Architecture Software Developers Manual Volume 3: System Programming Guide”(英特尔免费分发的一本指南)的“ Debugging and Performance Monitoring”一章包含了相关的技术细节。现有的所有调试器都可分为两大类。第一类调试器利用处理器提供的调试工具,而第二类调试器自行仿真处理器并完全控制所调试程序的执行过程。程序是既不能检测到也不能绕过

3、高质量仿真调试器的。可是,在本书写作时还不存在功能完备的针对奔腾处理器的仿真器,而且也不太可能会很快出现。但是,是否有必要开发这类仿真调试器呢?奔腾处理器提供了一系列的控制功能,它们甚至可以控制特许操作代码!它们支持单步执行程序,捕获位于指定地址的指令执行,并提供对指定的内存单元(或者输入/输出端口)的访问,以及任务切换等功能。如果标志寄存器的追踪位被置位,那么执行每一条机器指令后都会产生调试中断 INT 1,并将控制传给调试器。通过分析标志寄存器,被调试的代码能够检测到追踪。因此,为了保证其操作不被发现,调试器必须识别读取标志寄存器的指令,并通过返回值为零的追踪位来仿真其执行结果。必 须 注

4、 意 如 下 的 一 种 重 要 情 形 : 在 执 行 了 修 改 SS寄 存 器 的 指 令 之 后 , 并 不会 引 发 调 试 异 常 。 调 试 器 必 须 知 道 如 何 识 别 这 种 情 形 , 并 自 行 在 下 一 条 指 令 上设 置 断 点 。 否 则 , 追 踪 者就 不 能 进 入 指 令 POP SS后 面 的 过 程 ( 例 如 : PUSH SS; POP SS; CALL MySecrectProc) 。 并不是所有的当代调试器都具有这一功能的,因此,虽然这一点已被发现了很长时间,但这种技巧可能依然有效。有四个调试寄存器(即 DR0DR3 ),它们用于存放四

5、个断点的线性地址,而控制寄存器 DR7 包含了这四个断点中的每一个的控制条件。当满足该条件时,处理器产生 INT 0x1 异常并将控制传给调试器。总共有四个中断的条件:当执行指令时发生中断,如果内存单元被修改则发生中断,当读取或者修改但不执行内存单元时发生中断,以及当访问输入/输出端口时发生中断。通 过设置特定的数据位,就可以在调试寄存器被访问的任何时间引发调试异常。即使是特许执行代码试图读取(或者修改)这些寄存器时,也会引发调试异常。无论被 调试的代码有怎样的执行特权,设计精良的调试器都能够隐藏自己的行迹而不让被调试的代码发现自己(虽然如果操作代码调试自己,那么调试器不能使用全部的四 个断点

6、)。如果任务的任务状态段(TSS)的 T 位被置位,那么每当切换到该任务时,在执行该任务的第一条指令之前,都会引发调试异常。为了防止正被调试的代码发现调试器的存在,调试器应该追踪访问其 TSS 的所有操作,并把虚构的数据返回程序。必须注意:基于性能的考虑,Windows NT 并不使用 TSS(或者更精确地说,在一个硬件任务的所有运行时间内,它都只使用一个 TSS),因此在这种情形中,调试可能是无效的。软件断点是惟一的一种不编写功能完整的处理器仿真器就不能被隐藏的对象。它表示为一个字节的代码 0xCC,放在指令之前,当出现任何企图执行它的操作时都将引发 INT 0x3 异常。对于被调试的程序,

7、计算求和校验位就足以发现是否至少设置了一个断点。可以使用诸如MOV,MOVS, LOAS,POP,CMP 或者 CMPS 之类的指令来实现这一点,因为没有一种调试器能够追踪和仿真全部的这类指令。我们强烈地建议,只有在硬件断点不满足需要时,才使用软件断点。然而,实际上,所有的当代调试器(包括 SoftIce)都默认地设置软件断点而不是硬件断点。这一事实可以成功地用于保护机制。在第 12 章“如何防止追踪”一节中提供了几个例子,以说明这种机制的实现方法。当产生调试异常(与其他的异常一样)时,处理器将标志寄存器和下一条要执行指令(或者当前的指令,取决于异常类型)的地址压入堆栈。只有进行了这一操作,控

8、制才会传给调试器。在实模式中,带有返回地址的标志被放在被调试程序的堆栈中。所以,发现被调试是非常容易的。只须控制位于堆栈指针上的堆栈内容的完整性就足够了。另一种方法是,将堆栈指针指向堆栈的顶部,这样就不能往堆栈中增加任何新数据,调试器也就不能进行调试了。保护模式中的操作与此完全不同。异常处理程序可以驻留在调试器自己的地址空间中,不使用调试程序的任何资源(包括堆栈)也能进行调试。从理论上来说,一个设计精良的保护模式调试器是不能被检测到、也不能被阻止的。即使是运行在 ring 0 上的特许运行代码也不能做到这一点。上述的情形适用于 Windows NT,但不适用于 Windows 9x,因为后者不

9、能有效地利用保护模式的全部优点。相反,运行在 Windows 9x 中的程序不论是否被调试,其堆栈都会被弄乱。在开始接触 GNU 调试器(GDB)(一个与 MS-DOS 中的 D 类似的调试器,但是其功能更强大)时,Windows 爱好者们会觉得失望和难受。大量的文档更是使人沮丧,使人觉得无从下手。这真是石器时代!而 UNIX 爱好者又如何在这个原始社会的严酷环境中生存呢?这是一个难解之谜。每当看到几行 UNIX 的 源代码就会使我回想起前几年的窘境,那时没有工具可以进行任何方式的交互调试,程序出错后,惟一的改错方法就是内存转储。程序员不得不花上几个月的时间来 研究打印出来的大量信息,以及把出

10、错的代码聚集在一起并进行分门别类的分析。稍后,出现了通过打印和日志来调试的方法。这种方法将打印命令放在程序中的所 有关键位置上,将最重要的变量的内容打印出来。如果出现错误,打印在纸上的内容将揭示出程序出错之前在做什么,其结果又是如何。通过打印来进行调试的方法一直是一种重要的调试方法。在 Windows 世界中,这种方法主要应用于程序的调试版(参见清单 2.1),其中的打印命令在发行版中则被删除(参见清单 2.2)。这实在不是一种好习惯,因为如果最终用户遇到了故障,那么他们就只能得到错误时的内存转储,而这并不能提供多少信息。这就是 UNIX 提供大量日志控制系统 从标准的 syslog 直到高级

11、的企业时间日志(http:/ 的原因。它们减轻了进行输出和记录日志的工作量,并且在相当大程度上提高了程序的运行速度。通过打印来进行调试的方法能够满足 80%的调试需求。不论怎么说,调试器主要都是用来确定程序在指定的问题上是如何运行的:它有助于发现如果发生条件跳转的话,函数将返回什么值,变量包含哪些值等信息,在需要的位置插入 fprintf 和 syslog,并检查结果,就这么简单!清单 2.1 使用打印调试法的一个不好的例子清单 2.2 使用打印调试法的一个好的例子人并不是计算机的奴隶!相反,人们发明计算机是为了使人类的工作自动化。令人遗憾的是,在 Windows 世界中,情形并非如此。因此,

12、UNIX 尽可能地自动化处理错误检测过程。将编译器的警告级别设置为最高级或者使用独立的代码校验程序(最著名的代码校验程序是 Lint,图 2.1 显示了其界面),那么错误就会像老鼠逃离沉船似的离开你的程序。Windows 的编译器也能产生错误信息,并不比 GNU C 编译器(GCC)所产生的错误信息逊 色 ; 但 是 , 令 人 遗 憾 的是 , 由 于 大 多 数 程 序 员 的 编 程 水 平 很 低 , 他 们 并 不 看 重 这 些 信 息 。图 2.1 Lint 正在清除错误在 UNIX 环境中,只有在非常严重的情形下,当其他的工具都证明没有用处时,才会使用单步来执行程序和断点。Wi

13、ndows 爱好者们认为这种方法太陈旧和不方便。但这主要是因为 Windows 调试器有效解决的那些问题根本不会在UNIX 中出现。Windows 和 UNIX 的编程文化之间的差异是很大的。因此,在指责任何一方之前,先检讨一下自己。“不寻常”并不意味着“不正确”。当 UNIX爱好者需要在 Windows 下工作时,他们也同样会觉得不舒服。2.1 Ptrace 是 GDB 的基础调试工具GDB 是一个与系统无关的跨平台调试器。与大多数其他的 UNIX 调试器一样,它也是基于 ptrace 库来实现的,ptrace 库实现了底层的调试原语。为了调试多线程和并行的应用程序,我们推荐使用其他的库,或

14、者,使用诸如TotalView(http:/)这样专用的调试器更好。这是因为处理多线程不是 GDB 的强项。ptrace 能够将进程切换到挂起状态然后再继续执行进行,从被调试进程的地址空间中读取或者向其中写入数据,从中央处理单元(CPU)寄存器中读取或者向其中写入数据。在 i386 中,这类寄存器包括一般用途的寄存器、段寄存器、“协处理器”寄存器、XMMx 家族的流 SIMD 扩展寄存器(SSE),以及 DRx 家族(组织硬件断点时需要它们)的调试寄存器。在 Linux 环境中,还有其他的方法可用于操作被调试进程的辅助结构以及追踪系统调用。传统的 UNIX 系统没有这些功能,调试器必须自己实现

15、这些功能。清单 2.3 列出的例子说明了 ptrace 在程序中的用法。清单 2.3 使用打印调试法的一个好的例子上述的例子统计 ls 工具所使用的机器指令数量。如果要在 Linux 下编译这个例子,请用 PTRACE_TRACEME 替换 PT_TRACE_ME,用PTRACE_SINGLESTEP 替换 PT_STEP。2.1.1 Ptrace 及其命令在用户模式中,虽然只有一个函数可用,即 ptrace(int _request, pid_t _pid, caddr_t _addr, int _data),但是这个函数能做所有的事情!如果你愿意,也可以花费几个小时来编写自己的小调试器,以

16、解决特定的问题。ptrace 函数的_request 参数是最重要的一个参数,因为它确定你将做什么。BSD 和 Linux 的头文件使用不同的定义,这使得将 ptrace 应用从一个平台移植到另一个平台变得很复杂。默认地,我们使用 BSD 头文件中的定义。 PT_TRACE_ME(PTRACE_TRACEME)将当前进程切换到停止状态。它通常总是与 fork/exec 一起使用,虽然也能遇到自我追踪的应用程序。对于每一个进程,P T_TRACE_ME 只 能 被 调 用 一 次 。 追 踪 一 个 正 被 追 踪的 进 程 是 会 失 败 的 ( 另 一 个 较 不 重 要 的 结 果 是 进

17、 程 不 能 追 踪 它 自 己 。 如果 要 这 样 做 , 应 该 首 先 从 自 身 派 生 一 个 进 程 ) 。 大 量 的 反 调 试 技 术 都 是以 这 一 事 实 为 基 础 的 。 为 了 克 服 这 类 技 术 , 必 须 使 用 绕 过 ptrace 的调 试 器 。 一 个 信 号 被 发 送 到 正 被 调 试 的 进 程 , 并 将 该 进 程 切 换 到 停 止 状态 , 该 进 程 可 以 使 用 从 父 进 程 上 下 文 中 调 用 的 PT_CONTINUE 和PT_STEP 命令从停止状态退出。 wait 函数会延迟父进程的执行,直到被调试的进程切换为

18、停止状态或者终止为止(终止时,返回值为1407)。其他的所有参数都 被 忽 略 。 PT_ATTACH(PTRACE_ATTACH )将进程标志为 pid 的运行进程切换为停止状态,在这种情形下,调试器进程成为“父进程”。其他的所有参数都被忽略。进程必须具有与调试进程相同的用户标志(UID),并且不能是 setuid/setduid 进程(否则就要用 root 来调试)。 PT_DETACH( PTRACE_DETACH) 停 止 进 程 标 志 为 pid 进 程 ( 由PT_ATTACH 和 PT_TRACE_ME 指 定 ) 的 调 试 , 并 继 续 其 常 态 运 行 。 其他 的

19、所 有 参 数 都 被 忽 略 。 PT_CONTINUE(PTRACE_CONT )继续进程标志为 pid 的被调试进程的执行,而不中断与调试器进程的通信。如果 addr 1(在 Linux 中为 0),从上次停止的地址继续执行;否则,从指定的地址继续执行。参数_data 指定发送到被调试进程的信号数量(零说明没有信号)。 PT_STEP(PTRACE_SINGLESTEP)进行进程标志为 pid 的进程的单步执行,即执行下一条机器指令并切换为停止状态(在 i386 中,这是根据设置追踪标志来实现的 , 虽 然 有 些 “黑 客 ”函 数 库 使 用 硬 件 断 点 ) 。 BSD要 求 将

20、 参 数 addr 置 为 1, 而 Linux 要求将该参数置为 0。其他的所有参数都被忽略。 PT_READ_I 和 PT_READ_D(PTRACE_PEEKTEXT 和PTRACE_PEEKDATA)分别从代码区和正被调试进程的地址空间区读取机器字。在许多当代的平台中,这两个指令是等价的。ptrace 函数接收目标地址 addr,并返回读到的结果。 PT_WRITE_I 和 PR_READ_D(PTRACE_POKETEXT 和PTRACE_POKEDATA)将由 _data 传入的机器字写入 addr 所指定的地址。 PT_GETREGS,PT_GETFPREGS 和PT_GETDB

21、REGS(PTRACE_GETREGS,PTRACE_ FPREGS 和PT_GETFPXREGS)将一般用途寄存器、段寄存器和调试寄存器的值读入到地址由_addr 指针所指定的调试器进程的内存区中。只有 i386 平台接收这些与系统相关的命令。寄存器结构的描述放在头文件machine/reg.h 文件中。 PT_SETREGS,PT_SETFPREGS 和PT_SETDBREGS(PTRACE_SETREGS,PTRACE_ SETFPREGS 和PT_SETFPXREGS)通过拷贝由 _addr 指针所指定的内存区域的内容来设置被调试进程的寄存器的值。 PT_KILL(PTRACE_KIL

22、L)将 sigkill 发送到被调试进程,以终止其执行。2.1.2 GDB 的多线程支持要确定你的 GDB 版本是否支持多线程,可以使用 info thread 命令(它将输出关于线程的信息)。要在线程之间进行切换,可以使用 thread N 命令。如果不支持多线程,可以升级到 GDB 5.x 或者安装与你的 UNIX 版本一起提供的特别补丁,或者独立发布的补丁。推荐使用的调试并行应用程序的调试器是 TotalView(如图 2.2 所示)。清单 2.4 不支持多线程应用程序的调试清单 2.5 支持多线程应用程序的调试图 2.2 专用于并行应用程序的 TotalView 调试器2.1.3 GD

23、B 简明指南GDB 是一个基于传统命令行形式实现的控制台程序(其界面如图 2.3 所示)。虽然在 GDB 的长期进化中,GDB 接受了大量优秀的图形用户界面(GUI)(如图2.4 和图 2.5 所示),但 Turbo Debugger(TD)风格之类的交互调试目前在UNIX 世界中依然不流行。这通常都是从 Windows 平台移植程序的人的致命错误,这些人的思维方式不可避免地贴上了“M$”标签。这里简单的模拟并不适用:如果 TD 是长凳工具,那么 GDB 就是具有编程控制的车床。你迟早都会喜欢它的。为了在源代码级进行调试,程序必须在编译时包含调试信息。在 GCC 中,使用命令行选项 g 就能实

24、现这一点。如果没有调试信息,GDB 将在反汇编级上进行调试。被调试文件的文件名通常都是通过命令行来传递的,其格式如下:gdb 文件名 。如果要调试一个活动的进程,请在命令行中指定其进程标志。如果要调试内核转储,请使用命令行选项 core 内核名 。这三个参数可以同时使用,还可以使用命令行命令 target 来在它们之间进行切换。命令行命令 target exec 切换到被调试的文件,target child 切换到附加的进程,target core 切换到内核转储。可选用的命令行参数q 用于禁止显示版权信息。图 2.3 传统的 GDB 界面将应用程序加载到调试器之后,必须设置断点。请使用 br

25、eak 命令来设置断点(其简称是 b)。例如,命令 b main 将断点设在 C 语言的 main 函数上,而 b _start 将断点设在可执行和链接格式( ELF)文件的入口点(有的文件的入口点具有不同的名称)。也可以将断点设置在任何地址上,比如,b *0x8048424 或者 b *$eax。寄存器的名称要用小写字母,并以美元符号为前缀。GDB 理解两种“跨平台”的寄存器:$pc 用于命令指针,而$sp 用于堆栈指针。但是请记住,程序被装载到调试器之后并不能立即看到寄存器。只有用命令 run(r)启动被调试的进程后,寄存器才会显示出来。调试器执行决定应该设置软件断点还是硬件断点。最好不要

26、干涉这一过程。并非所有的调试器版本都支持强制设置硬件断点的的命令 hbreak。例如,我所用的版本就不支持该命令。用于数据的断点在 GDB 中称为观察点。当地址addr 处的数据被改变时,命令 watch addr 将调用调试器。当读或写操作访问地址 addr 时,命令 awatch addr 将调用调试器。当读操作访问地址 addr时,命令 rwatch addr 将调用调试器,但并非所有版本的调试器都支持这一命令。使用命令 info break 可以查看所有已设置的断点和观察点。命令clear 删除所有的断点,而命令 clear addr 删除所有设置在给定的函数、地址或者行号上的断点。诸

27、如 enable 和 disable 之类的命令用于暂时地允许和禁止断点。还有一种常规命令的高级语法用于处理断点,可以在文档中找到该语法的详细描述。命令 continue(c)用于继续执行被命令 b 所中断的程序,即从断点继续执行。图 2.4 调试器 DDD 给 GDB 提供的图形用户界面图 2.5 加在 GDB 上的另一种图形用户界面命令 next N(n N)执行下面的 N 行代码,会越过嵌套的函数,而命令step N(s N)会进入嵌套的函数。如果没有指定 N,则只执行一行代码。命令 nexti 和 stepi 的功能与它们相似。不同的是,这两条命令用于机器指令,而不是源代码的行数。它们

28、通常与命令 display/i $pc(x/i $pc)一起使用,这条命令要求调试器显示当前的机器指令。每一次会话只需调用一次该命令就足够了。命令 jump addr 将控制转到程序的任意位置,而命令 call addr/fname 调用参数 fname 所指定的函数。即使是 SoftIce 也没有提供这一命令!它是非常有用的,也是用户常常要求提供的命令。另外几条很有用的命令包括 finish,它用于在退出当前函数之前中断执行(它等效于 SoftIce的命令 P RET),以及命令 until addr(u addr),它用于继续执行直到到达指定的位置为止。如果启动命令时没带参数,则当下一条命

29、令到达时停止执行(这对循环特别重要)。命令 return 立即取消函数的执行。命令 print expression(p expression)用于输出指定表达式(例如 p 1+2)的值、指定变量的内容(p my_var)、指定寄存器的内容(p *$eax)或者内存单元的内容(p *0x8048424, p *$eax)。如果需要输出几个单元,就使用命令 x/Nh addr,其中 N 是要输出的单元的数量。此时并不需要在地址前面加上星号。命令 info register(i r)输出所有可用寄存器的值。修改内存单元和寄存器的内容 可 用 命 令 set来 实 现 。 例 如 , set $ea

30、x = 0将 0 写 入 寄 存 器eax。 命 令 set var may_var = $ecx 将寄存器 ecx 的值赋给变量my_var,而命令 set unsigned char* 0x8048424 = 0xCC 将数字0xCC 写入字节地址。命令 disassemble _addr_from _addr_to 以反汇编清单的形式输出内存的内容,表示的格式由命令 set disassembly-flavor 确定。命令 info frame,info args 和 info local 分别显示堆栈帧,函数参数和局部变 量 的 内 容 。 为 了 切 换 到 父 函 数 的 帧 ,

31、使 用 命 令 frame N。 命 令backtrace( bt) 所 做 的 事 情 与 Windows 调 试 器 的 命 令 call stack 相 同 。当 研 究 内 核 转 储 时 , 这 是 不 可 或 缺 的。用 GDB 进行工作的一个完整过程大致如下:将程序装入调试器,执行命令 b main(如果不成功,则使用 b _start),然后执行命令 r。用命令 n 或者 s一步一步地调试程序。如果需要,指定 x/i $pc 使 GDB 显示当前所执行的代码。执行命令 quit(q)以退出调试器。可在文档中找到所有其他命令的详细描述。希望这里列出的 GDB 命令简明指南能对你有

32、所帮助,而不至于迷失在信息丛林中。比较 UNIX 和 Windows 的调试器,马上就能发现:后者比前者落后许多,而且能够证明 Windows 调试器并不是针对专业人士的。三维显示的按钮、可以缩放的图标、弹出式菜单和其他过于花哨的东西,确实很好看。然而,需要不停地按键,你不觉得厌烦吗?在 GDB 中,通过编写宏(或者使用已有的宏)可以使事情变得方便许多,因为可以编程实现的每一件事在此都已经实现编程,而且可以免费使用。UNIX 中的调试工具功能强大而且使用方便。除了 GDB 外,还有一些其他的产品。惟一缺少的东西是可以基于没有符号信息和源代码的二进制文件工作的高质量系统级内核调试器。有一段时期

33、UNIX 存在多个平台,而且需要进行不同平台之间的移植,这在 UNIX 中留下了一个忧郁记号,以及实现可移植性和跨平台支持的愿望。在这种条件下,破解当然是很困难的!不过,源程序的可用性可以减少一些困难。2.1.4 追踪系统调用追踪系统调用打开了一扇真实窗口,透过它可以了解正被研究的程序的内幕。它可以显示正被调用的函数的名称,它们的参数和返回值。对于编程新手来说,有一个共同的问 题 是 常 常 忘 记 “额 外 的 ”错 误 检 查 , 而 调 试 器 不 是 查 找这 类 错 误 的 最 佳 工 具 。 可 以 使 用 truss 或者 ktrace 之类的标准工具,或者使用商业代码分析程序的

34、自由软件版本。清单 2.6 显示了使用 truss 所获得的日志。该程序试图打开名称为my_good_file 的文件,但是找不到该文件,从而导致程序出错。这是最简单的案例。然而,一个著名的规则认为全部开发时间的 99%花在寻找错误上,而这些错误都是不值得查找的!清单 2.6 使用 truss 查找错误2.1.5 相关链接 GDB 内幕(http:/gnuarm.org/pdf/gdbint.pdf):一本关于 GDB 内幕的极好的指南。当需要改善源程序时,它非常有用。 用 ptrace 追 踪 进 程( http:/ : 一 篇 关 于 在Linux 中 使 用 最 简 单 的 追 踪 程

35、序 中 的 例 子 来 进 行 追 踪 的 论 文 ( 在 FreeBSD中 的 情 形 完 全 不 同 ) 。 在源程序中修正漏洞(http:/www.linux- 使用 CTrace 库(http:/):一篇关于使用该库来调试多线程应用程序的论文。 内核和用户空间调试技术(德语文章)(http:/www.unfug.org/files/debugging.pdf):专门论述调试,描述少为人知的 GDB 结构细节的论文集。 ELF/INTEL 系统的逆向工程(法语文章)(http:/www.sstic.org/SSTIC03/arcticles/ SSTIC03-Vanegue_roy-Re

36、verse_Intel_ELF.pdf):关于在没有源程序时在 i386 平台上研究和调试ELF 文件的论文。从黑客的观点来看,UNIX 缺少像样的破解工具,而且这类工具也不会很快地出现。只有赤手空拳和绞尽脑汁地进行破解。最令人烦恼的是缺少功能完备的调试器,如果没有 SoftIce,至少也应该有 OllyDbg 之类的调试器啊。你必须自己编写内存转储程序、各种补丁和大包文件的自动解包程序之类的小工具,因为在网上找不到任何像样的东西。能找到的只有无数长时间以来失败项目的墓地。 我希望这种情形在未来几年内会有所改善。需求刺激供给。尽管如此,然而我在此还是给出一个关于现有的适合破解的软件的概述(顺便

37、提一下,复杂的保护机制也同样需要调试)。2.2.1 调试器再重复一次,GDB 是基于 ptrace 库的、跨平台、源代码级调试器,主要用于调试具有源代码的应用程序。它几乎不适用于破解。它支持程序执行的硬件断点,但是不支持对内存的读、写操作(但是,当从 VMware 中 启动时,硬件断点有效),也不能暂停和修改共享内存(换句话说,它几乎不能用于调试使用共享内存的程序)。它缺少内存查找功能。它不能装载文件,如果该文 件具有非法的段表结构、没有段表结构或者具有被删除段表。它被实现为一个控制台程序,带有一套复杂的命令系统,其命令系统的完整描述包括 300 页的小字体文字。如果你愿意,也可以采用该调试器

38、的图形外壳(有许多的版本)。可是,当需要更正一个糟糕的内核时,一个好的界面并没有太大的帮助。在其长期的演化中,GDB 变得与许多的反调试技术混杂在一起,大多数的这类技术仍保持其重要性。然而,GDB 也有其优点。首先,它是免费的,依照 GNU 的版权许可来发布(因此,它的名称为 GNU 调试器)。其次,它被作为大多数 UNIX 发布版本的一部分而一起提供,允许对其执行文件进行修补升级(进行修补升级时,甚至不需要退出该调试器)。图 2.6 显示了其运行情况。图 2.6 GDB 的工作界面下 面 是 新 手 须 知 的 一 个 小 技 巧 : 为 了 在 入 口 点 上 暂 停 , 需 要 首 先

39、确 定 入 口 点的 地 址 。 为 实现这一目标,可以使用标准工具 objdump(仅适用于未被保护的文件),或者 biew/ IDA:objdump file_name f。 然 后 将 被 调 试 的 程 序装 载 到 GDB( gdb q file_name) , 并启动命令 break *0xXXXXXXXX,其中 0xX 是起始地址。再运行命令 run 来启动程序执行。如果所有这些都正确完成,GDB 将马上暂停并将控制传给你。如果没 有 出 现 这 种 情 形,那么请在 BIEW中打开文件,并在入口点插入断点(代码为 CCh),请先记下原来的内容 。 再启 动 调 试 器 , 当

40、达 到 断 点 时 , 恢 复 其 内 容 ( set (char) *0xXXXXXXXX = YY)。汇编语言调试器(ALD,如图 2.7 所示)(http:/ ptrace 库实现,也具有该库的局限性。现在,它仅在 x86 平台上成功运行,并能在下列平台上成功编译:Linux,FreeBSD,NetBSD 和 OpenBSD。它支持执行的断点、单步执行、查看和编辑转储,以及查看和修改寄存器。它包含一个简单的反汇编器。这些就是它的全部功能!这实在是用于破解工具的最低功能要求。即使是 MS-DOS的 D 也提供了更丰富的功能集。然而,ALD 是免费软件,提供了源代码,并能装载没有段表的文件。

41、它适合于学习破解技术。然而,不能将它看成是主要的破解工具。图 2.7 ALD 调试器Dude(http:/the- ptrace。当 GDB 和 ALD 开始走向失败时,它却成功了。令人遗憾的是,它只能在 Linux 中运行,其他操作系统的用户都非常眼红。从系统体系结构来说,它包括如下三个部分:内核模块 the_dude.o,实现了底层调试功能;包装库 lib-duderino.so;以及外部用户界面,ddbg。一般最好从重新编写用户界面开始。该调试器是自由软件,下载之前,必须先在http:/ 上注册。Linice(http:/ SoftIce 在 Linux 中的模拟软件(如图 2.8 所示

42、)。这是一个功能强大、用于调试没有源代码的二进制文件的内核级调试器。这是任何 Linux 黑客必备的主要工具。现在它只能在内核 2.4版中运行(并且大概也能在 2.2 版中运行)。当在所有其他版本中编译时,它会在文件 iceface.c 中出错。它会增加设备/dev/ice,因此会透露它的存在(然而,这并不是一个问题,因为有源程序可用)。它弹出+快捷键(现在还不支持通用系列总线(USB)键盘),并且它没有装载程序(而且也不会很快出现)。因此,惟一的调试方法就是将机器命令 INT 03(操作码为CCh)插入入口点,过后需要手工恢复原来的内容。PIce( http:/ 是 Linux 中 的 一

43、个 试 验 性 内 核 级调 试 器 , 只 能 在 控 制 台模式下运行(换句话说,不能使用 X-Windows),实现了最小功能集。但是,它可能很有用。图 2.8 不,这不是在做梦。这是 SoftIce 的 Linux 版图 2.9 所示的 x86 仿真器(http:/ida-)是一个仿真调试器,这是一个 IDA Pro 插件,并没有进行预编译,而是以源程序的形式发布(这意味着除了 IDA Pro 还需要 SDK,似乎更难找到)。该仿真器的主要优点是它允许在虚拟处理器上执行任何代码段。例如,它可以将控制传给检查序列号和密码的过程,而绕过其他的代码。这样的技术结合了静态和动态分析的最好特色,

44、极大地简化了破解非常复杂精密保护机制的过程。图 2.9 主要的仿真器控制板2.2.2 反汇编工具一直以来 IDA Pro(http:/ Linux 版本!FreeBSD 和其他操作系统的用户只能在仿真器下使用其控制台 Windows 版本(如图 2.10 所示),或者必须在 MS-DOS,OS/2 和Windows 上工作。直到现在,IDA Pro 仍 然 不 支 持 没 有 段 表 的 文 件 。 然 而 , 这个 问 题 已 经 在 最 新 的 版 本 中 得 到 了 解 决 。 由 于 在 UNIX 中缺乏像样的调试器,因此 IDA Pro 成为主要的破解工具。工具 objdump 是与

45、 dumpbin 类似的一个工具,用于处理 ELF 文件,并带有一个简单的反汇编器。它要求文件具有段表,并且其中不能有非法的字段。它不能处理压缩文件。然而,如果你手头没有 IDA Pro 的话,这个工具还是有用的。图 2.10 IDA Pro 的控制台版本2.2.3 侦查软件truss 是一个很有用的工具,可以在大多数的 UNIX 发布包中找到(如图2.11 所示)。它应用程序级别追踪系统调用和被调试的程序发出的信号。这样能够收集许多关于保护机制内部的有用信息。清单 2.7 truss 报告样例ktrace 是发行包所提供的另一个工具。它追踪系统调用、名称翻译、输入/输出操作、信 号 、 用

46、户 模 式 追 踪 , 以 及 从 内 核 级 别 进 行 在 被 调 试 程 序 的 上 下 文切 换 。 总 起 来 说 , ktruss是 truss 的一个增强版本。可是,与后者相比,它产生二进制格式的报告,而不是文本格式的报告。因此,要得到报告就还需使用工具 kdump。图 2.11 使用 truss 追踪系统调用清单 2.8 ktrace 报告样例2.2.4 十六进制编辑器BIEW(http:/ ELF 文件阅读器等功能于一身的工具(如图 2.12 所示)。它缺少一个内置的汇编器,因此,必须直接在机器码级别上进行破解。当然,这是一个不合理的漏洞。可是,并没有更好的替代工具(惟一的最

47、大可能就是自己编写一个汇编器)。图 2.12 BIEW 十六进制编辑器2.2.5 内存转储程序在 UNIX 中,每一个进程的内存内容都表现为驻留在/proc 目录中的文件集的形式。此外,寄存器内容和其他数据也存放在该处。然而,内存转储还不是直接可用的 ELF 文件,也不适合于直接使用。但是,可以反汇编其原始映像。2.2.6 自动保护工具可执行文件的压缩文件不仅可以用来减小程序的大小,也可以用于保护文件免于被破解。在 Windows 中,这样的做法并不能把程序的破解推迟多少时间。但是在 UNIX 中就不一样!在 UNIX 中没有自动的解压缩程序,只有很少的几种存储程序,并且没有像样的调试器(特别

48、是对于 Linux 之外的系统)。因此,使用压缩文件就足够了,能够破解它的黑客并不会太多。为了破解它,黑客们需要很强的动机(一般都没有这样的动机)。所有压缩文件都存在的一个严重问题是,它们极大地减小了被保护程序的可移植性(当它们包含特别的反调试技术时,情形更严重)。此外,我在实际应用中遇到的压缩程序都只适 用 于 Linux, 不 能 用 于 FreeBSD 和 其 他 UNIX 版 本( 虽 然 编 写 这 样 的 压 缩 程 序 是 可 能 的 ) 。Shiva(http:/.au/)是能够使用的功能最强大的压缩程序,虽然它是基于 Windows 程序员很久以前就知道的陈旧思想而实现的。它基于以“洋葱层”模型为基础的一种多层加密模型来实现,使用了带有反调试和反汇编技术的多态引擎,可以抵御 GDB 和其

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 专业基础教材

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报