深入理解计算机系统bomb实验
一.实验目的
通过此次实验,提高阅读和理解汇编代码的能力,学习使用gdb调试工具。
二.实验资源
从实验课程网站上下载的bomb压缩包。实验环境为虚拟机中的linux系统。
三.实验要求与准备
<1>本次实验为熟悉汇编程序及其调试方法的实验。
<2>实验内容包含2个文件bomb(可执行文件)和bomb.c(c源文件)。
<3>使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。
<4>分析汇编代码,找到在每个phase程序段中,引导程序跳转到 “explode_bomb”程序段的地方,并分析其成功跳转的条件,以此为突破口寻找应该在命令行输入何种字符通关。
<5>本实验一共有7个关卡,包括6个普通关卡和1个隐藏关卡。要求至少通过6个普通关卡
1.直接在linux系统中用浏览器登录实验课程网络,下载bomb实验相关文件夹。可执行文件bomb,C语言文件放在桌面。
2. cd+Desktop进入桌面文件夹,对bomb反汇编得到汇编代码放在1.txt文件中。
2. 尝试运行文件。发现无法正常运行,显示没有权限,通过查阅相关资料找到了一个解决办法:
选中bomb->properties->permissions->选择allow executing file as program
四.实验任务
Phase 1
1.思路分析与原理设计
<1>前面几行是开辟栈:
push %ebp ebp入栈
mov %esp,%ebp esp始终指向栈顶元素,此时ebp为栈顶
Sub $0x18,%esp 开辟一段新的空间
<2>找到跳转到<explode_bomb>的代码段
向前找到<explode_bomb>的条件:<string_not_equal>,即两个字符串内容不相同时
要比较的两个字符串分别是:
立即数0x804a15c地址中的内容和0x8(% ebp),即我们要输入的字符串。
2.利用gdb查看地址0x804a15c的内容,得到通关语句
实验心得
观察汇编代码,发现调用了<string_not_equal>函数,可以推测输入的是一串字符串,通过x/s以字符串的形式查看地址0x804a15c所对应的值,输入该字符串即可通过关卡。字符串比较函数是通过将两个字符串进行比较,将结果存到%eax中,最后判断%eax的值。
phase 2
1.思路分析与原理设计
发现在这段代码中,有几个调用函数的地方:
<1>8048d7f call 804910b<read_six_numbers>
调用名为read_six_numbers的函数,读入输入的六个数,查看该函数的汇编代码:
0804910b <read_six_numbers>:
push %ebp :%ebp入栈
mov %esp,%ebp:使%esp保存当前栈顶的地址,即指向%ebp
sub $0x28,%esp:为当前函数开辟一个栈帧
mov 0xc(%ebp),%eax
lea 0x14(%eax),%edx
mov %edx,0x1c(%esp):保存第六个数
lea 0x10(%eax),%edx
mov %edx,0x18(%esp):保存第五个数
lea 0xc(%eax),%edx
mov %edx,0x14(%esp):保存第四个数
lea 0x8(%eax),%edx
mov %edx,0x10(%esp):保存第三个数
lea 0x4(%eax),%edx
mov %edx,0xc(%esp):保存第二个数
mov %eax,0x8(%esp):保存第一个数
movl $0x804a232,0x4(%esp)
<2>在8048d90处调用<explode_bomb>
栈的开辟阶段
读入六个数
将第一个数放在了-0x20(%ebp)的位置,并比较第一个数与0的大小,若相等则继续,否则炸弹爆炸,第二个数在-0x1c(%ebp)的位置,比较第二个数与1的大小,若相等,则继续,否则炸弹爆炸。所以可以确定要输入的第一个数是0,第二个数是1.
将地址-0x18(%ebp)->%ebx
将地址-0x8(%ebp)->%esi
mov -0x4(%ebx),%eax %ebx保存相对%ebp偏移量为24(0x18的十进制数)的地址,该地址-4传给%eax,即%eax中保存相对%ebp偏移量为28
add -0x8(%ebx),%eax 将相对ebp偏移量为28和32的内存中的数相加,即将第一个数0,第二个数1,相加,得到第三个数1,存入%eax
esi为循环结束条件,%ebp中的值加4,即将第二个和第三个数相加,得到第四个数,第三个和第四个数相加,得到第五个,第四个数和第五个数相加,得到第六个数。这段汇编代码先确定了前两个数的值,然后通过循环,后面的数等于它前面两个数之和依次确定这六个数。
2. 得到通关语句
循环结束,所以要输入的六个数分别是:0 1 1 2 3 5
输入这六个数,通关
2. 实验心得
先确定第一二个数,这部分比较简单,然后add -0x8(%ebx),%eax ,将两个数相加,结果保存在%eax,%esi控制循环,%ebx=%ebx+4,改变参加运算的两个数的地址。
Phase 3
1.思路分析与原理设计
<1>栈的建立过程
8048ea1: 55 push %ebp
8048ea2: 89 e5 mov %esp,%ebp
8048ea4: 83 ec 28 sub $0x28,%esp
<2>
8048ea7: 8d 45 f0 lea -0x10(%ebp),%eax
8048eaa: 89 44 24 0c mov %eax,0xc(%esp)
8048eae: 8d 45 f4 lea -0xc(%ebp),%eax
8048eb1: 89 44 24 08 mov %eax,0x8(%esp)
8048eb5: c7 44 24 04 3e a2 04 movl $0x804a23e,0x4(%esp)
8048ebc: 08
8048ebd: 8b 45 08 mov 0x8(%ebp),%eax
8048ec0: 89 04 24 mov %eax,(%esp)
执行完这些步骤后的栈:
<3>call调用函数[email protected]
调用函数结束后,可以看到当%eax>1时,跳转,否则引爆炸弹,推测eax为函数[email protected]的返回值,则需要输入一个以上的数字。
<4>输入数字
根据这两条汇编语句推测,输入的第一个数字保存在地址-0xc(%ebp)中,且不大于7.
<5> switch部分
进入switch语句,根据输入的不同数字,跳转到不同的地址,具体的地址要使用gdb查看。
在gdb中,用p/x *(int *)(0x804a1a0+4*输入的数字)查看对于所有输入输入的跳转地址。
继续向下进行:发现第一个输入的数字必须小于等于5,第二个数必须和%eax中的值相等,即根据输入的第一个数跳转到不同地址,对%eax中的数进行相关操作,产生一个新值,输入的第二个数必须和这个数相等。
根据跳转地址,计算第二个数。
i=0时,跳转到0x8048f12
8048f1e: 2d 5a 03 00 00 sub $0x35a,%eax
8048f23: 05 ef 02 00 00 add $0x2ef,%eax
8048f28: 2d 16 02 00 00 sub $0x216,%eax
8048f2d: 05 16 02 00 00 add $0x216,%eax
8048f32: 2d 16 02 00 00 sub $0x216,%eax
8048f37: 05 16 02 00 00 add $0x216,%eax
8048f3c: 2d 16 02 00 00 sub $0x216,%eax
经计算的到第二个数应为147
8048f41: eb 0a jmp 8048f4d <phase_3+0xac>//跳转到地址8048f4d
这句汇编代码要求输入的第一个数字不大于5
i=1时,跳转到0x8048f19
根据这些语句计算出第一个数为1时,第二个数应为-641
可以看到进行完计算的操作后,都要跳转到地址8048f4d即第一个输入数字不能大于5.
2.得到结果
3. 实验心得
使用了switch分支结构,根据输如的不同数字跳转到不同位置进行不同位置进行运算,最后得到结果。首先分析输入数字的范围,然后通过不同的输入判断跳转的位置来准确计算。