关键词:
编译原理
反汇编技术
80x86指令系统
代码分离
摘要:
引言 在计算机应用日益普及的今天,我们所面临的对诸如软件的生存期、硬件的二次开发、对系统的理解分析等方面的问题也越来越多。现在的软件的规模、结构也变得庞大而复杂。其生存期也更长了。从头开始建大系统的数量急剧减少,很多遗产被逐步利用起来。再生工程以少的开销、短的时间、低的风险把旧系统改造为一个新的形式,使系统在操作、系统能力、功能、性能、可维护性和可支持性上得到提高。软件再生工程中的逆向工程是指对现有系统进行分析,以最大的努力去建立比代码抽象层次更高的表达形式。如从目标代码分析求出源代码,由源代码求详细设计与总体设计,由总体设计求软件需求分析。反汇编技术是实现从目标代码到源代码翻译的较好的途径。 编译技术在反汇编中的应用 编译系统主要的应用是将高级语言书写的程序翻译成等价的机器语言程序或汇编语言程序。在逻辑上由分析和综合两大部分组成。分析部分主要包括词法分析、语法分析和语义分析;综合部分主要包括存储分配、代码优化和代码生成,有时还包括生成中间代码。编译程序的分类主要包括诊断型、优化型、可重定目标型、交叉型和增量型。 由于微处理器指令的执行过程严格受指令计数器指针PC的控制。程序存储器空间中(即目标代码中)的单元是否为指令由该单元是否被PC访问,或PC是否访问该单元的前面某个单元并且其代码长度区间是否包含此单元。该原则为区分代码与数据提供了充分必要的判断依据。通过对PC值的跟踪记录,将PC访问的程序存储器单元(目标代码)置代码标志而不被PC访问的单元置数据标志可以实现程序存储器中代码与数据的分离。因此为了对目标代码进行数据与代码的分离,必须遍历PC的所有可能的流向及路径。传统的方法中采用了链表数据结构对PC值进行跟踪记录,它的遍历算法复杂、编程实现困难,且链表搜索时间和分支判断数量呈指数增长。在大的反汇编程序设计时这种链表数据结构不再适合。通过对目标代码结构的分析和指令控制模型的建立,我们发现用栈或队列的数据结构比链表数据结构优越:程序设计逻辑清晰、遍历算法易于实现。 为了分离指令和数据,反汇编程序与汇编程序及软件仿真程序不同,它对转移指令和调用指令作同样的处理,因为转移指令的两分支必然都是代码,所以在遍历转移指令的两分支之一后应返回到另一分支并遍历该分支。CPU的指令解释中转移指令和调用指令对PC的操作基本一致,但存在下条指令地址压栈与不压栈的区别。对于分离代码与数据的反汇编程序,转移指令的另一分支方向地址也必须压栈。 反汇编的方法 由于计算机的主要工作是进行数据处理,故80x86指令系统中的多数指令对应于操作码。对于不同的操作数有不同的方法来存取它们;特别是存放在存储单元的操作数,可以采用多种不同的方式来寻找地址以便进行数据存取。 80x86指令系统的编码格式非常紧凑并且灵活,通常第一字节位操作码,用以规定操作的类型,第二字节规定操作数的寻址方式。典型的单操作数指令结构如下: 操作码 reg 操作数在16位寄存器内 操作码w Mod 次操作码 r/m 操作数在寄存器或存储器 典型的双操作数指令结构如下图: 操作码dw ModRegR/m 典型的双操作数指令 其中:reg---寄存器寻址代码。 Mod---寻址方式代码,配合r/m一起使用。 Mod=11,寄存器寻址 Mod≠11,存储器寻址 r/m---寄存器或存储器寻址方式(与mod字段组合使用) d位---指示操作数的传送方向,用于双操作数指令: d=0时,reg字段为源操作数,r/m和mod字段为目的操作数 d=1时,r/m和mod字段为源操作数,reg字段为目的操作数 w位---字操作标志位: w=0时,字节操作标志 w=1时,字操作标志 为了便于分析,我们定义一个子语言。该子语言是基于80x86系统的一 个子集。我们将子语言的汇编助记符分为两个域:操作码域和操作数域。指令系统包括数据传输类指令、算术运算类指令、逻辑运算类值指令、转移指令、堆栈操作指令。寻址方式包括寄存器寻址、存储器寻址。把子语言的指令集分为6类操作:数据传输、算术运算、移位/循环,串处理、位控制、控制转换。所有指令都可能带有0、1、2或3个操作数,操作数可能驻留在寄存器中,或隐含域指令本身,或存在与存储器中。大多数指令操作数(如cli、sti)只占一个字节,一个操作数的指令一般都是2个字节长。平均指令长度是3.2个字节。为了实现反汇编,定义指令反汇编表格式如下: [分类][反汇编方式][指令助记符] 指令的排列按指令操作码位顺序,每条指令占用16个字节。具体的分类及反汇编方式如下: 第一类:单字节指令或指令前缀: 反汇编方式‘0’:指令前缀,直接输出指令。 反汇编方式‘1’:单字节指令,直接输出指令。 第二类:二字节指令: ‘0’:直接输入指令;‘1’:指令=助记符+8