关键词不能为空

当前您在: 主页 > 英语 >

Bomblab实验报告

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-17 23:24
tags:

-

2021年2月17日发(作者:考林)





课程实验报告













称:



计算机系统原理实验




实验项目名称:


BombLab









级:



计科


1501





名:



马子垚






号:


2








间:


2017



4



19









实验目的



理解汇编语言,学会使用调试器



实验原理



二进制炸弹是作为一个目标 代码文件提供给学生们的程序,运行时,


它提示用户输入


6


个不同的字符串。


如果其中任何一个不正确,


炸弹


就会“爆炸”:打印出一条错误信息。学生通过反汇编和逆向工程来


确定是哪六个字符串,从而解除他们各自炸弹的雷管。



实验步骤及体会



一、



实验准备



1




首先,


由于虚拟机操作系统与


windows


系统相互独立,



以首先将


Bomb. c


及其相关文件存至百度云盘,然后在


ubantu

< p>
操作系统内下载至文件系统目录下的下载文件夹


里面:




2




输入< /p>


./bomb


试运行


bomb.c


文件时会提示权限不够:




所以需要输入


chmod+x+


文件名的命令



改变文件或目录


的访问权限。


用它控制文件或目录的访问权限。


在经过操


作之后,获 得了权限,程序可以正常运行了:



3




由于< /p>


bomb.c


文件并没有蕴含许多能破解的信息,所以需


要将其进行反汇编,详细操作如下:



输入


objdump



d bomb >


将汇编代码输出到 自


动生成一个



的文件里面,方便我们查 看与分析


代码:





二、



具体实验步骤及其分析



试查看导出的



,我发现总的文本里面分为很多段,其中就


Phase_1~Phase_6


Phase_defuse



Phase_secret< /p>


以及其他相关函


数的代码,所以我猜测每一段

Phase


代码就是我们需要破解的关卡,


所以我将它们分 别导出新建


text


文件,逐段分析。





Phase_1

< br>及其破解过程




知识点:


string


,函数调用,栈



反汇编代码及其分析:



08048f61 :


8048f61:


55


push %ebp



//


压栈


ebp


为栈指针


esp


为栈指针。



push


指令可以理解为两个步骤:



1. subl $$4 , %esp


–栈底向下移一位



2. movl %ebp


,(


%esp


< p>



ebp


的值存入


esp




8048f62:


89 e5


mov %esp,%ebp


//


esp


赋值给


ebp


8048f64:


83 ec 18


sub $$0x18,%esp


//esp

< p>
指针下移


0x18


个字节



8048f67:


c7 44 24 04 5c a1 04


movl $$0x804a15c,0x4(%esp)


//



0x804a15c


处 的内容存入


*



%esp+4




//


前一段总的分 析:


初始化栈,


push


是压栈指令,


ebp


寄存器中的内容是栈底指针。


e sp


寄存器里面存的是栈顶地址。


通过


sub


指令,


将栈顶固定在第十八个存储单元

< br>里面。


字符串的内存地址为


0804a15c

< p>


四个字节。


一个存储单元存放

< br>8



bit


两个


十六进制数字,栈顶及后三个单元存栈顶地址。故用


mov

指令将字符串地址存在


栈顶后的第四个字节处。



8048f6e:


08


8048f6f:


8b 45 08


mov 0x8(%ebp),%eax


//


取用户输入的内容存入


%ebp


8048f72:


89 04 24


mov %eax,(%esp)


//eax


的值赋值给


*esp


8048f75:


e8 31 00 00 00


call 8048fab


//


调用字符串比较函数,


string_no t_equal


就提示需要比较两个字符串,


通过


gcc


函数调用规范知道压入堆栈的两个参数分别为输入字符串与程序内部字 符串。



函数的时候, 必须返回


%eax



0


,而这个函数的作用就是


判断字符串是否相等。



8048f7a:


85 c0


test %eax,%eax


//

< br>test


执行的就是


and


的指 令,


不会保存


and


执行的结果,


而是根据


and


的结果设置


flags


寄存器的各种标志。




8048f7c:


74 05


je 8048f83


// Je


指令:当


ZF


等于零的时候跳转,也就是相等的时候跳转。判断


%eax

< br>是否为


0


,为


0


跳转,不为


0


引爆




8048f7e:


e8 4e 01 00 00


call 80490d1


8048f83:


c9


leave


//


为结束函数做准备



8048f84:


c3


ret



//



返回。


ret


:不带任何参数时,用于在子程序的结束位置,被调用的子程序必


须有


ret


指令,


否则调用没 有


ret


指令的子程序会导致自陷,


子 程序执行完之后


处于失控状态。带参数


ret n

< p>
表示子程序返回主程序的同时,堆栈弹出


n


个字< /p>


节(栈顶指针减


n




具体操作:


< p>
读取


0x804a15c


内存的字符串(密码), 并设置断点。




成功读取到字符串,作为密码输入:




第一关成功破解




程序流程:



1


、取内存地址


0x8049a04


处的内容;



2


、取用户输入的内容(即密码);



3


、比较两者的值,相等则


%eax< /p>


置为


0


,进入下一关,不相等则调用引爆 程序引


爆炸弹。



< br>Phase_2


及其破解过程




知识点:循环语句,数组



08048d6a :


8048d6a:


55


push %ebp


8048d6b:


89 e5


mov %esp,%ebp


8048d6d:


56


push %esi


8048d6e:


53


push %ebx


8048d6f:


83 ec 30


sub $$0x30,%esp


8048d72:


8d 45 e0


lea -0x20(%ebp),%eax



//



lea



取偏移地址将一个数的内存单元的偏移地址,


送入寄存器中存 储,



mov


不同的是


mov


是将该数当作一个偏移地址存入寄存器,没有寻址的功能。



8048d75:


89 44 24 04


mov %eax,0x4(%esp)


//


取用户输入的内容,存入


%ebp


8048d79:


8b 45 08


mov 0x8(%ebp),%eax


8048d7c:


89 04 24


mov %eax,(%esp)


//


前面几行均为初始化栈操作



8048d7f:


e8 87 03 00 00


call 804910b

< p>
//


调用函数


read_six_numbers


(说明密码是


6


个数字)




8048d84:


83 7d e0 00


cmpl $$0x0,-0x20(%ebp)


//


比较第一个输入的的数是否为


0


8048d88:


75 06


jne 8048d90

//


若不为


0


< br>jne


看出)则跳转下一步(爆炸)



//


比较第二个输入的的数是否为


1


8048d8a:


83 7d e4 01


cmpl $$0x1,-0x1c(%ebp)


//


若为


1



je


)则跳转下一步(


8048d95




8048d8e:


74 05


je 8048d95


8048d90:


e8 3c 03 00 00


call 80490d1


8048d95:


8d 5d e8


lea -0x18(%ebp),%ebx


8048d98:


8d 75 f8


lea -0x8(%ebp),%esi


8048d9b:


8b 43 fc


mov -0x4(%ebx),%eax


//


下一个数必须是前两个数之和




8048d9e:


03 43 f8


add -0x8(%ebx),%eax


8048da1:


39 03


cmp %eax,(%ebx)


//


若为前两个数之和则跳转下一步(


8048daa


), 否则爆炸



8048da3:


74 05


je 8048daa


8048da5:


e8 27 03 00 00


call 80490d1


8048daa:


83 c3 04


add $$0x4,%ebx


8048dad:


39 f3


cmp %esi,%ebx


//

回到


8048d9b


,相当于一个循环

,


esi



edx


都是地址寄存器,首先将两个地址比


较,当他们相同的时候,就一直向下,否 则跳转。



8048daf:


75 ea


jne 8048d9b


8048db1:


83 c4 30


add $$0x30,%esp


8048db4:


5b


pop %ebx


8048db5:


5e


pop %esi


8048db6:


5d


pop %ebp


8048db7:


c3


ret




总体上看,这一关让我们输入的 是以


0


为首项的


Fibonacc


数列。



密码


: 0 1 1 2 3 5


故在第一个的基础上:





程序流程:



1.

读取用户输入内容(为


6


个数字);



2.


判断输入的第一个值是否为


0


,不是则引爆炸弹;



3.

< p>
判断输入的第一个值是否为


1


,不是则引爆炸弹;



3.


做一个


6


次循环,判断后一个数是否等于前两个数之和,不是则引爆炸弹;


4.


六个数字判断相等结束后,进入下一关





Phase_3


及其破解过程




知识点:


switch


语句



08048ea1 :


8048ea1:


55


push %ebp


8048ea2:


89 e5


mov %esp,%ebp


8048ea4:


83 ec 28


sub $$0x28,%esp


//


初始化栈的操作



8048ea7:


8d 45 f0


lea -0x10(%ebp),%eax


//


用户输入的参数


2


存在

*



%ebp-10


< p>


8048eaa:


89 44 24 0c


mov %eax,0xc(%esp)


//


用户输入的参数


1


存在


*



%ebp-c


< br>



8048eae:


8d 45 f4


lea -0xc(%ebp),%eax


8048eb1:


89 44 24 08


mov %eax,0x8(%esp)


//


取出地址


0x804a23e


中的内容



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)



8048ec3:


e8 78 f9 ff ff


call 8048840 <__isoc99_sscanf@plt>


//


调用


sscanf

函数,传入输入参数,


scanf


函数调用之后可能导致< /p>


eax


里面的保存的


数据发生改变。



8048ec8:


83 f8 01


cmp $$0x1,%eax


//

scanf


读入数据流之后,


%eax

寄存的是读入数据的个数。



8048ecb:


7f 05


jg 8048ed2


8048ecd:


e8 ff 01 00 00


call 80490d1


8048ed2:


83 7d f4 07


cmpl $$0x7,-0xc(%ebp)



//

必须


-0xc(%ebp)


的值小于


7


,否则爆炸



8048ed6:


77 6b


ja 8048f43


8048ed8:


8b 45 f4


mov -0xc(%ebp),%eax


//


相当于


switch(a)



8048edb:


ff 24 85 a0 a1 04 08


jmp *0x804a1a0(,%eax,4)


//


跳转到以地址


*0x804a1a0


为基址的跳转表中



8048ee2:


b8 00 00 00 00


mov $$0x0,%eax


8048ee7:


eb 53


jmp 8048f3c


8048ee9:


b8 00 00 00 00


mov $$0x0,%eax


8048eee:


66 90


xchg %ax,%ax


8048ef0:


eb 45


jmp 8048f37


8048ef2:


b8 00 00 00 00


mov $$0x0,%eax


8048ef7:


eb 39


jmp 8048f32


8048ef9:


b8 00 00 00 00


mov $$0x0,%eax


8048efe:


66 90


xchg %ax,%ax


8048f00:


eb 2b


jmp 8048f2d


8048f02:


b8 00 00 00 00


mov $$0x0,%eax


8048f07:


eb 1f


jmp 8048f28


8048f09:


b8 00 00 00 00


mov $$0x0,%eax


8048f0e:


66 90


xchg %ax,%ax


8048f10:


eb 11


jmp 8048f23


8048f12:


b8 14 03 00 00


mov $$0x314,%eax


8048f17:


eb 05


jmp 8048f1e


8048f19:


b8 00 00 00 00


mov $$0x0,%eax


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


8048f41:


eb 0a


jmp 8048f4d


8048f43:


e8 89 01 00 00


call 80490d1


8048f48:


b8 00 00 00 00


mov $$0x0,%eax


//

输入的第一个数字必须小于


5,


否则爆炸

< br>



8048f4d:


83 7d f4 05


cmpl $$0x5,-0xc(%ebp)


8048f51:


7f 05


jg 8048f58


//


表示输入的第二个数字必 须等于第一个数经过数次运算的结果



8048f53:


3b 45 f0


cmp -0x10(%ebp),%eax


//


成功跳出



8048f56:


74 05


je 8048f5d


8048f58:


e8 74 01 00 00


call 80490d1


8048f5d:


c9


leave


8048f5e:


66 90


xchg %ax,%ax


8048f60:


c3


ret


帧堆栈数据记录表:





具体分析:




movl $$0x804a23e,0x4(%esp)


,用


gdb


查看


0x804a23e


的值




发现这个应该是要求输入两个数字




jmp *0x804a1a0(,%eax,4)


首 先,


看到这个语句不知道是什么意思,


搜索也搜索不到这段地址 。


后来通过百


度,发现这是一个基于跳转表的

< br>switch


语句的汇编描述,后来决定自己编写一


个< /p>


switch


语句去运行,理解了


swi tch


语句在汇编中的表达方式。





当第一个数输入

< br>0




去查看

< br>*0x804a1a0+4*0=*0x804a1a0


的值



所以会跳转到


0x8048f 12


那行代码中



则后面的运算为


0x(314-35a+2ef-216).


转换为十进制为


147


。故输入


0 147


得到结果(接第二个炸弹)





同理:





输入


1



运算得到


1 -641






输入


2



运算得到


2 217


(运行结果略)






输入


3



运算得到


3 534


(运行结果略)






输入


4



运算得到


4 0


(运行结果略)




程序流程:



1.

读取输入参数


1


和参数


2


,调用


ssanf


函数传入两个参数;



2.


比较参数


1



7


的大小,小于等于

7


继续,大于


7


引爆;

< p>


3.


根据参数


1


的值来搜索跳转地址,计算得到最终的


%eax




4.


比较参数

< br>1



5


的大小,小于等于


5


继续,大于


5


引爆 ;



5.


比较参数

2


与计算得到的


%eax


是否相等 ,


相等则进入下一关,


不相等则引爆。




Phase_4


及其破解过程




知识点:递归



08048e2e :


8048e2e:


55


8048e2f:


89 e5


8048e31:


83 ec 28


8048e34:


8d 45 f0


8048e37:


89 44 24 0c


8048e3b:


8d 45 f4


8048e3e:


89 44 24 08


8048e42:


c7 44 24 04 3e a2 04


8048e49:


08


8048e4a:


8b 45 08


8048e4d:


89 04 24


//


调用


sscanf


函数,传入输入参数




8048e50:


e8 eb f9 ff ff


8048e55:


83 f8 02


//


输入的为两个数据,否则引爆



push %ebp


mov %esp,%ebp


sub $$0x28,%esp


lea -0x10(%ebp),%eax


mov %eax,0xc(%esp)


lea -0xc(%ebp),%eax


mov %eax,0x8(%esp)


movl $$0x804a23e,0x4(%esp)


mov 0x8(%ebp),%eax


mov %eax,(%esp)


call 8048840 <__isoc99_sscanf@plt>


cmp $$0x2,%eax


8048e58:


75 0c


jne 8048e66


8048e5a:


8b 45 f4


mov -0xc(%ebp),%eax


8048e5d:


85 c0


test %eax,%eax


//


第一个数字为负数则跳转至爆炸




8048e5f:


78 05


js 8048e66


8048e61:


83 f8 0e


cmp $$0xe,%eax


//

第一个数字必须小于


14


则跳转,否则继续运行至爆炸




8048e64:


7e 05


jle 8048e6b


8048e66:


e8 66 02 00 00


call 80490d1


8048e6b:


c7 44 24 08 0e 00 00


movl $$0xe,0x8(%esp)


8048e72:


00


8048e73:


c7 44 24 04 00 00 00


movl $$0x0,0x4(%esp)


8048e7a:


00


8048e7b:


8b 45 f4


mov -0xc(%ebp),%eax


8048e7e:


89 04 24


mov %eax,(%esp) < /p>


//


调用


func4

函数



8048e81:


e8 da fc ff ff


call 8048b60


//



phase_ 4


调用


func4


时,


phase_4


中的返回地址被压入栈中,形成


Pha se4


的栈


帧的末尾。


Func4


从保存栈指针的值开始。



8048e86:


83 f8 01


cmp $$0x1,%eax



8048e89:


75 06


jne 8048e91


8048e8b:


83 7d f0 01


cmpl $$0x1,-0x10(%ebp)


//


第二个数不为


1


则跳转

8048e91


(爆炸)



//< /p>



1


则成功跳出




8048e8f:


74 0c


je 8048e9d


8048e91:


8d b4 26 00 00 00 00


lea 0x0(%esi,%eiz,1),%esi


8048e98:


e8 34 02 00 00


call 80490d1


8048e9d:


c9


leave


8048e9e:


66 90


xchg %ax,%ax


8048ea0:


c3


ret



与之前一样




movl $$0x804a23e,0x4(%esp)


句来得到要求,要求输入两个数字





分析题目



设要求输入的两个数字为


x,y


,则


0 <=x<14,y=1.



调用


fu nc4


函数,故查看


func4


的代码 :



08048b60 :


8048b60:


55


8048b61:


89 e5


8048b63:


83 ec 18


8048b66:


89 5d f8


push %ebp


mov %esp,%ebp


sub $$0x18,%esp


mov %ebx,-0x8(%ebp)


8048b69:


89 75 fc


mov %esi,-0x4(%ebp)


//


%ebx



%esi


是被调用者保存寄存器,


func4


函数在使用这些寄存器的值之前,


必须把他 们保存到栈中,


并且在返回前回复他们,


在本题中保存的这些值 没有实


际用途。



8048b6c:


8b 55 08


mov 0x8(%ebp),%edx


8048b6f:


8b 45 0c


mov 0xc(%ebp),%eax


8048b72:


8b 5d 10


mov 0x10(%ebp),%ebx


8048b75:


89 d9


mov %ebx,%ecx


8048b77:


29 c1


sub %eax,%ecx


8048b79:


89 ce


mov %ecx,%esi


//


shr


是逻辑右移指令



8048b7b:


c1 ee 1f


shr $$0x1f,%esi


8048b7e:


8d 0c 0e


lea (%esi,%ecx,1),%ecx


8048b81:


d1 f9


sar %ecx


//sar


是算数右移指令,


只移位一位,算术右移补最高位




8048b83:


01 c1


add %eax,%ecx


8048b85:


39 d1


cmp %edx,%ecx


8048b87:


7e 17


jle 8048ba0


8048b89:


83 e9 01


sub $$0x1,%ecx


8048b8c:


89 4c 24 08


mov %ecx,0x8(%esp)


8048b90:


89 44 24 04


mov %eax,0x4(%esp)


8048b94:


89 14 24


mov %edx,(%esp)


8048b97:


e8 c4 ff ff ff


call 8048b60


8048b9c:


01 c0


add %eax,%eax


8048b9e:


eb 20


jmp 8048bc0


8048ba0:


b8 00 00 00 00


mov $$0x0,%eax


8048ba5:


39 d1


cmp %edx,%ecx


8048ba7:


7d 17


jge 8048bc0


8048ba9:


89 5c 24 08


mov %ebx,0x8(%esp)


8048bad:


83 c1 01


add $$0x1,%ecx


8048bb0:


89 4c 24 04


mov %ecx,0x4(%esp)


8048bb4:


89 14 24


mov %edx,(%esp)


//


递归调用函数


func4



8048bb7:


e8 a4 ff ff ff


call 8048b60


8048bbc:


8d 44 00 01


lea 0x1(%eax,%eax,1),%eax


8048bc0:


8b 5d f8


mov -0x8(%ebp),%ebx


8048bc3:


8b 75 fc


mov -0x4(%ebp),%esi


8048bc6:


89 ec


mov %ebp,%esp


8048bc8:


5d


pop %ebp


8048bc9:


c3


ret


帧堆栈数据记录表:



Phase_4:



Fun4:



第一次调用


fun4()

< p>


edx


为输入的


x


值,


eax=0,ebx=14;


根据整个


fun4()


函数,



x<=7


时,调换


ebx


与< /p>


esi


的值,返回到第四关中,


eax! =1


,直接跳转最后爆


炸。所以,


x> 7


。当输入为


8


ecx=7


减去


1


变为

< p>
6


,然后自己调用自己,即为递


归函数,此时新的


edx=8


不变,


eax=0


不变,


ebx=6


。根据跟随代入法,最后可< /p>


以总结成


C


语言代码:

< br>


分析过程:






转化成的


C


代码:




将机器语言转化为


C


代码我们可以更直观的来判断和推导未知数 的值,


的出的结


论是这是一个二分法求值。

当且仅当下端和中值相等的时候才退出递归过程,



可能出 现在上半区,这题有多解。



由此可见,当输入


8 1


或者


9 1


或者


11 1


时,可避开炸弹。





主程序流程:



1.



读取参数,调用


sscanf


函数传入参数;



2.



比较输入参数与


0


的大小,小于等于则引爆;



3.



把输入参数传入函数

< p>
func4


,并调用函数


func4




4.



若第二个数等于


1


,则返回,进入下一关,否则引爆炸 弹。


-


-


-


-


-


-


-


-



本文更新与2021-02-17 23:24,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/663641.html

Bomblab实验报告的相关文章