实验准备
安装debian,并完成相关配置
1)更换apt源
2)配置SSH
在virtualBox网络选项卡中设置端口转发,名称SSH,主机端口22,子系统端口22
3)安装aptitude并用其代替apt命令(安装删除软件包时,易造成系统崩溃,aptitude处理依赖问题更佳)
4)安装如下软件包
- build-essential: 包含gcc,make,ld等开发工具
- gedit:文本编辑器
- gdb:交互式程序调试工具
- hexedit:二进制文件编辑器
- git:源代码版本控制工具
实验过程
1.进入管理员模式后,在home文件夹下新建homework文件夹,用于保存测试文件。
mkdir homework
2.修改homework文件夹权限,用于新建文件
chmod 777 /home/charon/homework
3.若计算机为x86-64位系统,为了编译成IA-32指令集,先运行以下命令
sudo aptitude install build-essential module-assistant
sudo aptitude install gcc-multilib g+±multilib
4.在homework文件夹下新建hello.c源程序。
5.编译
-
对hello.c的程序进行预编译,预编译时对源程序中以字符#开头的命令进行处理,例如将include后的.h文件内容嵌入到源程序文件中。预处理后的文件还是文本文件,以 .i 为扩展名。
gcc -E hello.c -o hello.i
-
对hello.i文件进行编译,生成汇编语言源程序文件,以 .s 为扩展名,此时还是为文本文件。
gcc -S hello.i -o hello.s
-
对hello.s文件进行汇编,生成一个可重定位目标文件,以 .o 为扩展名,此时为二进制文件,其中的内容为01表示的机器指令、数据和其他信息。
gcc -c hello.s -o hello.o
-
将多个可重定位的目标文件和标准库函数链接合并成一个可执行目标文件。在该例中,链接将hello.o文件和标准库函数printf所在的可重定位目标模块printf.o进行链接,生成可执行目标文件hello。
gcc hello.o -o hello
-
控制台中输入命令后,得到编译结果,文件中包含了相对应的文本文件和二进制文件。
①文本文件:用ASCII码存储的,文件中的每一个英文符号用8位的ASCII码表示。
②二进制文件;用01序列存储的机器指令,数据和其他信息。
③机器指令:计算机可识别和执行的,所以称为目标代码。
④可执行目标文件:假设为解决一个任务,把这个任务划分成多个子任务,为每个子任务编写一个程序,每个子任务的程序是一个独立的模块,这个程序模块汇编后就是一个可重定位目标文件。其由单个模块组成,不可直接执行。例如一个打印程序构成一个可重定位的目标文件,每个子任务的程序都写好,并且汇编成一个可重定位目标文件后,就可以组装这些程序文件,构成一个完整的可执行文件。这个组装的过程就是连接,链接后的文件就是可执行目标文件,可在计算机中直接执行。
也可以使用一条命令使c语言程序直接生成可执行目标文件。
gcc hello.c -o hello
gcc -o0 -m32 -g hello.c -o hello
为了调试程序的方便性,在用gcc命令编译c程序时,可能会加入’-o0’(表示编译时采用的优化级别,0表示不用编译优化)、-m32(表示编译成x86-32位的指令,如果你计算机是64位架构的处理器,不加这个选项就会被编译成x86-64位的指令集),g表示带调试信息(单步调试必须加入)。
6.objdump的使用
目标文件都是由01序列的机器指令、数据和其他信息构成,无法用文本编辑器打开,可以使用objdump来反汇编二进制的目标文件。对可重定位目标文件和可执行目标文件,都可反汇编。
-
新建gbdtest.c源程序
-
采用上述命令编译
对于生成的 ‘.o’ 可重定位目标文件和 ‘gdbtest’ 可执行目标文件,可以使用objdump命令进行反汇编。objdump -S gdbtest.o>gdbtesto.txt objdump -S gdbtest>gdbtest.txt
这里的-S表示在反汇编后的内容中添加源代码,方便理解c语言源程序与IA-32机器级指令之间的对应关系(建议-S选项和gcc中的-g选项配合使用)。>表示将反汇编后的内容保存到文件中,这里保存到方便阅读的文本文件。
-
反汇编
objdump -S gdbtest.o>gdbtesto.txt objdump -S gdbtest>gdbtest.txt
.o的可重定位目标程序的反汇编内容:中间一列为十六进制表示的机器指令,右边一列是机器指令反汇编后的汇编指令,左边一列为机器指令存储的地址(从0开始)。这是完成子任务的一个独立模块,所以每块的地址都是从0开始
可执行目标程序的反汇编内容:三列表示内容与上述相同,但指令地址不是从0开始。指令和数据都有一个确定的地址,是按照操作系统给定的存储器地址映射空间分配,也是在调试过程中可以看到的地址,不是内存的物理地址,而是一个虚拟地址。
7.GDB调试工具的使用
程序运行时的当前状态包括:
- 程序的当前断点位置:反应程序已经执行了哪些指令,下一步要执行哪一条指令。eip寄存器保存下一条将要执行指令的地址。ir 显示所有寄存器的内容,i r eip显示寄存器eip的内容。
- 通用寄存器的内容(数据编码表示):i r eax ebx ecx edx
- 存储器的单元内容(数据编码表示):x/8xb 0xffffd2bc, x/2xw 0xffffd2bc(x查看存储单元的内容,后跟一些参数选项。参数选项中的x表示存储单元内容用十六进制形式,b或w表示要显示的存储单元的宽度,b为按字节显示,w为按4字节显示。8或2表示要显示的数据单元的个数,十六进制值表示要显示的存储单元的起始地址。)
- 栈帧信息:IA-32用栈来支持过程的嵌套调用,过程的入口参数、返回地址,被保存寄存器的值、被调用过程中的非静态局部变量等都会被保存在栈中。系统为每个执行的过程分配一个栈空间,称为栈帧,用于保存过程执行时的数据信息。当前栈帧范围由esp栈顶指针和ebp栈底指针寄存器指出。当前栈帧字节数可以用y=R[ebp]-R[esp]+4计算得出。显示当前栈帧信息可以通过x/yxb $esp或x/zxw $esp,y为上述表达式的值,栈帧起始地址是esp指向的单元地址,z=y/4,显示从esp指向的地址开始。
-
打开GDB
gdb gdbtest
-
设置断点,使用函数名main。
break main run(执行程序) i r eip
eip地址后三位为11b5,到gdbtest.txt中查找,说明接下来要执行的指令为这一条,前面的已完成执行。通过反汇编内容知道:其将数3传送到0xc(%ebp)地址的存储单元,对应c语言的x=3这条语句。
-
查看运行状态
此时0xc这个地址应该为空,使用i r ebp查看ebp的值。
此时ebp的值末三位为278,-0x14(%ebp)=0xbffff264,也就是这个单元地址的末两位为0x64。x/4xb 0xbffff264(4个字节,十六进制表示,按字节显示) x/1xw 0xbffff264(按四字节显示。把4个字节作为一个信息组,计算机是小端方式)
上图表示了下条指令未执行前,该地址处内容为这些。 -
执行指令
si i r eip
当前eip值后四位为11bc,表示将要执行这条语句
x/4xb 0xbffff264 x/1xw 0xbffff264
查看之前单元的内容,可以发现已经有了变化。 -
退出
quit