拆炸弹实验报告.docx
《拆炸弹实验报告.docx》由会员分享,可在线阅读,更多相关《拆炸弹实验报告.docx(18页珍藏版)》请在冰点文库上搜索。
拆炸弹实验报告
课程实验报告
课程名称:
计算机组成与结构
实验名称:
拆炸弹
专业班级:
姓名:
学号:
完成时间:
(一)实验目的:
程序运行中有6个关卡(6个phase),每个phase需要用户在终端上输入特定的字符或者数字才能通关,否则会引爆炸弹!
那么如何才能知道输入什么内容呢?
这需要你使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。
然后分析汇编代码,找到在每个phase程序段中,引导程序跳转到“explode_bomb”程序段的地方,并分析其成功跳转的条件,以此为突破口寻找应该在命令行输入何种字符通关。
(二)实验环境:
虚拟机ubuntu系统下32位操作系统,终端,gdb工具。
(三)实验目的和操作:
拆炸弹准备操作:
查看给出的bomb.c中的代码,得知控制检测密码正误的6个函数分别为:
phase_1,phase_2,phase_3,phase_4,phase_5,phase_6。
使用gbd工具对可执行文件bomb进行反汇编:
打开bomb可执行文件所在的文件夹输入反汇编命令objdump-dbomb后,出现了大量汇编代码,在终端窗口可以看到很多行的汇编代码。
也可以使用汇编指令objdump-dbomb>1.txt将汇编代码输出到服务器上一个自动生成的叫1.txt的文件中。
开始拆炸弹:
:
的反汇编代码如下:
0x08048f61<+0>:
push%ebp
0x08048f62<+1>:
mov%esp,%ebp
0x08048f64<+3>:
sub$0x18,%esp
0x08048f67<+6>:
movl0x804a15c,0x4(%esp)//sp寄存器地址指向$0x804a15c
0x08048f6f<+14>:
mov0x8(%ebp),%eax将$0x804a15c的数据移给eax寄存器
0x08048f72<+17>:
mov%eax,(%esp)//函数条用的准备过程
0x08048f75<+20>:
callx8048fab
0x08048f7a<+25>:
test%eax,%eax//与这一行比较用户输入和$0x804a15c中的值
0x08048f7c<+27>:
je0x8048f83
0x08048f7e<+29>:
callx80490d1
0x08048f83<+34>:
leave
0x08048f84<+35>:
ret
通过分析代码可知:
phase_1的密码是固定储存在$0x804a15c中的。
令$0x804a15c中的数据与用户输入的数据比较,若相同则跳过explode_bomb,避开炸弹。
用命令x/s0x804a15c查看其中的数据如下:
输入WehavetostandwithourNorthKoreanallies.
结果为:
结果正确。
反汇编代码如下:
0x08048d6a<+0>:
push%ebp
0x08048d6b<+1>:
mov%esp,%ebp
0x08048d6d<+3>:
push%esi
0x08048d6e<+4>:
push%ebx
0x08048d6f<+5>:
sub$0x30,%esp
0x08048d72<+8>:
lea-0x20(%ebp),%eax
0x08048d75<+11>:
mov%eax,0x4(%esp)
0x08048d79<+15>:
mov0x8(%ebp),%eax
0x08048d7c<+18>:
mov%eax,(%esp)
0x08048d7f<+21>:
call0x804910b读取六个数字
0x08048d84<+26>:
cmpl$0x0,-0x20(%ebp)0和第一个数字比较,不相等则爆炸。
0x08048d88<+30>:
jne0x8048d90
0x08048d8a<+32>:
cmpl$0x1,-0x1c(%ebp)1和第二个数字比较,相等则跳过爆炸。
0x08048d8e<+36>:
je0x8048d95
0x08048d90<+38>:
call0x80490d1
0x08048d95<+43>:
lea-0x18(%ebp),%ebxebx指向第三个数字。
0x08048d98<+46>:
lea-0x8(%ebp),%esiesi指向第六个数字再向后移一位的地址。
0x08048d9b<+49>:
mov-0x4(%ebx),%eaxebx向前第一位的数字赋给eax。
0x08048d9e<+52>:
add-0x8(%ebx),%eaxeax再加上ebx向前第二位的数字。
0x08048da1<+55>:
cmp%eax,(%ebx)比较ebx前两位的和与ebx指向的数字。
0x08048da3<+57>:
je0x8048daa相等则跳过爆炸(explode_bomb)
0x08048da5<+59>:
call0x80490d1
0x08048daa<+64>:
add$0x4,%ebxebx地址向后移动一位(四个字节)。
0x08048dad<+67>:
cmp%esi,%ebx如果还未超过第六位数字,则跳转0x8048d9行。
0x08048daf<+69>:
jne0x8048d9b
0x08048db1<+71>:
add$0x30,%esp
0x08048db4<+74>:
pop%ebx
0x08048db5<+75>:
pop%esi
0x08048db6<+76>:
pop%ebp
0x08048db7<+77>:
ret
从上面的代码分析可知:
phase_2的密码需要输入六位数字,并且第一个是0,第二个是1,然后执行到0x8048d9b这一行代码发现往下运行会出现一个循环,两条重要指令是imull-0x4(%esi,%edx,4),%eax和cmp%eax,(%esi,%edx,4),其中%eax和%edx在每次循环中加1直到寄存器中值超过6,程序执行完毕。
在循环中执行的是一个数等于前两个数之和,寄存器ebx每次循环将向前第一位的数字赋给寄存器eax。
从而实现了计算前6位的斐波那契数列的值为:
011235。
执行结果如下:
反汇编代码如下:
0x08048ea1<+0>:
push%ebp
0x08048ea2<+1>:
mov%esp,%ebp
0x08048ea4<+3>:
sub$0x28,%esp
0x08048ea7<+6>:
lea-0x10(%ebp),%eax
0x08048eaa<+9>:
mov%eax,0xc(%esp)此处为第二个数字。
0x08048eae<+13>:
lea-0xc(%ebp),%eax
0x08048eb1<+16>:
mov%eax,0x8(%esp)此处为第一个数字。
0x08048eb5<+20>:
movl$0x804a23e,0x4(%esp)用x/s0x804a23e命令查看$0x804a23e为
0x08048ebd<+28>:
mov0x8(%ebp),%eax%d%d,即要输入两个整数,上面已经指出。
0x08048ec0<+31>:
mov%eax,(%esp)
0x08048ec3<+34>:
call0x8048840<__isoc99_sscanf@plt>
0x08048ec8<+39>:
cmp$0x1,%eax以上两行即要求输入至少两组数据,否则引爆。
0x08048ecb<+42>:
jg0x8048ed2
0x08048ecd<+44>:
call0x80490d1
0x08048ed2<+49>:
cmpl$0x7,-0xc(%ebp)第一个数大于7引爆,即第一个数小于等于7。
0x08048ed6<+53>:
ja0x8048f43
0x08048ed8<+55>:
mov-0xc(%ebp),%eax
0x08048edb<+58>:
jmp*0x804a1a0(,%eax,4)跳转至0x804a1a0+eax*4(第一个数)内数据所
0x08048ee2<+65>:
mov$0x0,%eax指的行数。
0x08048ee7<+70>:
jmp0x8048f3c
0x08048ee9<+72>:
mov$0x0,%eax
0x08048eee<+77>:
xchg%ax,%ax
0x08048ef0<+79>:
jmp0x8048f37
0x08048ef2<+81>:
mov$0x0,%eax
0x08048ef7<+86>:
jmp0x8048f32
0x08048ef9<+88>:
mov$0x0,%eax
0x08048efe<+93>:
xchg%ax,%ax
0x08048f00<+95>:
jmp0x8048f2d
0x08048f02<+97>:
mov$0x0,%eax
0x08048f07<+102>:
jmp0x8048f28
0x08048f09<+104>:
mov$0x0,%eax
0x08048f0e<+109>:
xchg%ax,%ax
0x08048f10<+111>:
jmp0x8048f23
0x08048f12<+113>:
mov$0x314,%eax当第一个数为0时跳转到此处,第二个数x=788。
0x08048f17<+118>:
jmp0x8048f1e跳转到0x8048f1e。
0x08048f19<+120>:
mov$0x0,%eax
0x08048f1e<+125>:
sub$0x35a,%eaxx=x-858
0x08048f23<+130>:
add$0x2ef,%eaxx=x+751
0x08048f28<+135>:
sub$0x216,%eaxx=x-534
0x08048f2d<+140>:
add$0x216,%eaxx=x+534
0x08048f32<+145>:
sub$0x216,%eaxx=x-534
0x08048f37<+150>:
add$0x216,%eaxx=x+534
0x08048f3c<+155>:
sub$0x216,%eaxx=x-534
0x08048f41<+160>:
jmp0x8048f4d跳转到0x8048f4d行。
0x08048f43<+162>:
call0x80490d1
0x08048f48<+167>:
mov$0x0,%eax
0x08048f4d<+172>:
cmpl$0x5,-0xc(%ebp)第一个数大于5引爆,即第一个数小于等于5。
0x08048f51<+176>:
jg0x8048f58
0x08048f53<+178>:
cmp-0x10(%ebp),%eax第一个数是0时,算得x=147,即第二个数
0x08048f56<+181>:
je0x8048f5d为147。
0x08048f58<+183>:
call0x80490d1
0x08048f5d<+188>:
leave
0x08048f5e<+189>:
xchg%ax,%ax
0x08048f60<+191>:
ret
从以上分析可知:
再往下然后看到jmp
输入指令x/s0x8049abb,得到0x8049abb:
"%d%d",显示出应该输入两个数字。
如果输入不满足两个数,则会引爆炸弹,并且第二个与第一个数的值相关。
第一个值得范围为0---5的整数,当第一个数输入的不是0---5的整数,也会引爆炸弹。
输入的第一个数存入在寄存器eax中,并且根据跳转指令jmp*0x804a1a0(,%eax,4)选择将要跳转的位置,执行相应的代码计算出第二个需要输入的值。
1)当第一个数输入的是0,eax寄存器中对应的值为0,则跳转到指令*0x804a1a0
用指令查看*0x804a1a0的值为:
发现*0x804a1a0对应的值为0x8048f12,找到0x08048f12对应的代码执行发现第二个数计算得结果为:
x=0x(314-35a–2ef–216+216-216+216-216+)=147
2)当输入的第一个数是1时,同理用指令查看*(0x804a1a0+4)的值为
则代码从0x08048f19对应的代码执行第二个数计算得结果为:
x=0x(-35a–2ef–216+216-216+216-216+)=-641
3)同理输入2,3,4,5对应的第二个数字为:
217,534,0,-534
结果打印为:
反汇编代码如下:
0x08048e2e<+0>:
push%ebp
0x08048e2f<+1>:
mov%esp,%ebp
0x08048e31<+3>:
sub$0x28,%esp
0x08048e34<+6>:
lea-0x10(%ebp),%eax
0x08048e37<+9>:
mov%eax,0xc(%esp)此处第二段数字。
0x08048e3b<+13>:
lea-0xc(%ebp),%eax
0x08048e3e<+16>:
mov%eax,0x8(%esp)此处第一段数字。
0x08048e42<+20>:
movl$0x804a23e,0x4(%esp)$0x804a23e内为%d%d,即输入两个整数。
0x08048e4a<+28>:
mov0x8(%ebp),%eax
0x08048e4d<+31>:
mov%eax,(%esp)
0x08048e50<+34>:
call0x8048840<__isoc99_sscanf@plt>
0x08048e55<+39>:
cmp$0x2,%eax以上两行要求之前输入的为两个数据,否则引爆。
0x08048e58<+42>:
jne0x8048e66
0x08048e5a<+44>:
mov-0xc(%ebp),%eax
0x08048e5d<+47>:
test%eax,%eax第一段数据大于等于0,否则引爆。
0x08048e5f<+49>:
js0x8048e66
0x08048e61<+51>:
cmp$0xe,%eax第一段数据小于等于14时跳过引爆。
0x08048e64<+54>:
jle0x8048e6b
0x08048e66<+56>:
call0x80490d1
0x08048e6b<+61>:
movl$0xe,0x8(%esp)
0x08048e73<+69>:
movl$0x0,0x4(%esp)
0x08048e7b<+77>:
mov-0xc(%ebp),%eax
0x08048e7e<+80>:
mov%eax,(%esp)
0x08048e81<+83>:
call0x8048b60这几行为运行一个递归函数func4来确定第一
0x08048e86<+88>:
cmp$0x1,%eax段数据的值。
0x08048e89<+91>:
jne0x8048e91
0x08048e8b<+93>:
cmpl$0x1,-0x10(%ebp)第二个数据等于1则跳过爆炸。
0x08048e8f<+97>:
je0x8048e9d
0x08048e91<+99>:
lea0x0(%esi,%eiz,1),%esi
0x08048e98<+106>:
call0x80490d1
0x08048e9d<+111>:
leave
0x08048e9e<+112>:
xchg%ax,%ax
0x08048ea0<+114>:
ret
以上对代码的分析可以看出,第二个数据确定为1。
第一个数据范围为大于等于0,小于14,并且由一个递归函数func4又一次缩小了范围。
但因为这个递归函数太过繁杂,而第一个数据范围又不大,直接将第一个数据从0尝试到13,得到了三个数字8、9、11符合要求。
因此密码有三组:
81,91,111。
反汇编代码如下:
0x08048db8<+0>:
push%ebp
0x08048db9<+1>:
mov%esp,%ebp
0x08048dbb<+3>:
push%esi
0x08048dbc<+4>:
push%ebx
0x08048dbd<+5>:
sub$0x20,%esp
0x08048dc0<+8>:
lea-0x10(%ebp),%eax
0x08048dc3<+11>:
mov%eax,0xc(%esp)此处为第二个数。
0x08048dc7<+15>:
lea-0xc(%ebp),%eax
0x08048dca<+18>:
mov%eax,0x8(%esp)此处为第一个数。
0x08048dce<+22>:
movl$0x804a23e,0x4(%esp)$0x804a23e内为%d%d,即输入两个整数。
0x08048dd6<+30>:
mov0x8(%ebp),%eax
0x08048dd9<+33>:
mov%eax,(%esp)
0x08048ddc<+36>:
call0x8048840<__isoc99_sscanf@plt>
0x08048de1<+41>:
cmp$0x1,%eax以上两行保证输入数据多于1组,否则引爆。
0x08048de4<+44>:
jg0x8048deb
0x08048de6<+46>:
call0x80490d1
0x08048deb<+51>:
mov-0xc(%ebp),%eax
0x08048dee<+54>:
and$0xf,%eax按位与,即保留第一个数的后四位。
0x08048df1<+57>:
mov%eax,-0xc(%ebp)
0x08048df4<+60>:
cmp$0xf,%eax第一个数二进制后四位不能为(1111)2,否则引爆。
0x08048df7<+63>:
je0x8048e22
0x08048df9<+65>:
mov$0x0,%ecx
0x08048dfe<+70>:
mov$0x0,%edx
0x08048e03<+75>:
mov$0x804a1c0,%ebx$0x804a1c0指向的是一个数组,下面分析。
0x08048e08<+80>:
add$0x1,%edx
0x08048e0b<+83>:
mov(%ebx,%eax,4),%eax
0x08048e0e<+86>:
add%eax,%ecx
0x08048e10<+88>:
cmp$0xf,%eax
0x08048e13<+91>:
jne0x8048e08以上五行为一个for循环语句,
0x08048e15<+93>:
mov%eax,-0xc(%ebp)
0x08048e18<+96>:
cmp$0xf,%edx上述的for循环要循环15次。
0x08048e1b<+99>:
jne0x8048e22
0x08048e1d<+101>:
cmp%ecx,-0x10(%ebp)确定第二个数的值,下面分析。
0x08048e20<+104>:
je0x8048e27
0x08048e22<+106>:
call0x80490d1
0x08048e27<+111>:
add$0x20,%esp
0x08048e2a<+114>:
pop%ebx
0x08048e2b<+115>:
pop%esi
0x08048e2c<+116>:
pop%ebp
0x08048e2d<+117>:
ret
从代码的分析中可知:
需要输入两个数,并且第一个数的后四位不能为1111,若为1111则引爆炸弹。
在程序中我们会遇到一个给定值0x804a1c0。
我们可以查看其值为:
查看16个值,因为发现后面的循环中需要循环遍历使用16个数。
其中最难懂的代码就是0x08048e0b<+83>:
mov(%ebx,%eax,4),%eax这一行代码,ebx对应的是0x804a1c0对应的首地址。
这句话的意思解释为C语言的意思就是n=a[n]。
寄存器ecx保存的是计算得sum的总值。
这段代码翻译成c语言类似于:
sum=0;
for(i=1;n=15;i++)
{
n=a[n];
sum+=n;
}
其中第一次n取的值应为用户输入的第一个数二进制下的后四位。
之后每次都等于a[n],sum为除去第一次之外所有n取值的和。
采用逆推的方法来求出n第一次取的值。
序号
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数组
10
2
14
7
8
12
15
11
0
4
1
13
3
9
6
5
for循环当n为15时终止,因此最后一个n为15,对应序号为上一个n,即6。
同理,可以得到,n从第一个到最后一个为:
5123711139480101214615。
sum=15*16/2-5=115。
即用户输入的第二个数为115。
用户输入第一个数二进制下的后四位为0101(即第一个n=5),
由于第一位只要满足输入的数的后四位不为1111,第二个数为115即可,如:
10115