Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit d127ace

Browse files
committed
Change the task/problem of coroutine
1 parent dd96499 commit d127ace

1 file changed

Lines changed: 30 additions & 15 deletions

File tree

docs/lab/lab3.md

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
栈帧相关实验。
1010

11-
本学期,我们仍然将金老师 ICS 第三个 Lab 回炉重造,添加更多讲解和小工具提示,以加深各位同学对栈帧和协程的理解,并探索一些相关应用,丰富同学们的知识面。
11+
本学期,我们仍然将金老师 ICS 第三个 Lab 回炉重造,减轻代码工作量并添加更多讲解和提示,以加深各位同学对栈帧和协程的理解,并探索一些相关应用,丰富同学们的知识面。
1212

1313
本次Lab由五个部分组成,含三个主题:
1414

@@ -136,12 +136,17 @@ void foo() {
136136

137137
> [!tip]
138138
>
139-
> 注意 cmd 字符串对应地址处的值在输入完后不变;
139+
> **注意**: cmd 字符串对应地址处的值在输入完后不变;
140+
>
140141
> 我们的目标类似于执行 `system("./malware")`,如果你已经构造成功了 payload 使程序执行 `system("./malware")`,但程序在 `system` 函数的内部崩溃了,这在我们的预期内。**也就是说,只需要见到 `You have successfully detonated the bomb! Congratulations!` 这一行就算通过实验。**
142+
>
141143
> 造成这种情况的原因是:`system` 内部某些汇编语句对栈的对齐要求很高,如果没有对齐至 0x10,就会导致程序崩溃。如果你使用 GDB 进行调试,你就可以看到这几条非常“挑剔”的汇编指令。
144+
>
142145
> 在正常执行一个函数时,`rsp` 寄存器是向 0x10 对齐的;调用某一个函数时使用的是 `call` 指令,会往栈上压入一个 8 字节的返回地址(这里就破坏了对齐),然后跳转到函数的起始位置。因此,每个函数都会假设自己刚刚被调用时,`rsp` 寄存器是不向 0x10 对齐的。
146+
>
143147
> 然而,我们通过劫持返回地址调用某个函数时,我们并没有使用 `call` 指令,而是直接跳转到了函数的开头。这就导致函数开始时,栈反而向 0x10 对齐了,这破坏了 `system` 函数的假设,导致程序崩溃。
144-
> 解决办法很简单:**不要直接调用 `system` 函数,而是调用一个中间函数,并且跳过函数开头的一条 `push rbp` 指令。**这类似于手动破坏栈的对齐,使得 `system` 函数可以正常执行。
148+
>
149+
> 解决办法很简单:不要直接调用 `system` 函数,而是调用一个中间函数,并且跳过函数开头的一条 `push rbp` 指令。这类似于手动破坏栈的对齐,使得 `system` 函数可以正常执行。
145150

146151
### 实验之后的思考题
147152

@@ -173,18 +178,19 @@ void foo() {
173178

174179
**Task 2.1** `dark-calc` 有一个未使用的 `grade_eval` 函数。尝试在阅读代码后,通过 `payload_dark.py` 构建新的 payload,**GDB**导入到程序 `dark-calc` 执行 `grade_eval`,输出大家理想的成绩!**提交 GDB 运行结果截图(有输出语句 `Hope A will be your grade!` 即可)与 `payload_dark.py` 作为评分依据。无自动评分。**
175180

176-
**Problem 2.1** 查询资料,解释为什么不存在<s>TA没有发现)</s>通过直接执行 `./dark-calc < payload_dark``python3 payload_dark.py | ./dark-calc` 执行 `grade_eval` 的方法。
177-
178181
> [!tip]
179182
>
180183
> 可以用与 Task 1.1 相似的方法保存 payload 并进行调试。
181184
>
182-
>GDB 中,默认程序的栈空间位置固定,我们需要在栈空间内执行代码,期望效果是**赋值输入参数****调用函数**
185+
>GDB 中,默认程序的栈空间位置在**一次重启期间**固定。
186+
> 我们需要在栈空间内执行代码,期望效果是**赋值输入参数****调用函数**
183187
>
184188
> 大家学过 CSAPP,打过 BombLab,想必对常用 x86-64 汇编指令的机器码有一些了解,此处给出 mov 和 jmp 指令的机器码:
185-
> - 64 位立即数移动 `mov $imm, %rxx``48 c7 [Mod+Reg+R/M](1 byte) [imm](4 byte)``Mod` 指定寄存器模式(`11` 为寄存器寻址),`Reg` 源操作数(这里是立即数,所以置为 0),`R/M` 指定目标寄存器;
186-
> - 跳转到 `rxx` 寄存器指向的地址`jmp *%rxx``ff [Mod+Reg+R/M](1 byte)``Mod` 指定寄存器模式(`11` 为寄存器寻址),`Reg` 设为 `100` 指定此指令为 `jmp` 指令,`R/M` 指定目标寄存器;
187-
> - <s>善用人工智能工具编写汇编代码。</s>
189+
>
190+
> - 64 位立即数移动 `mov $imm, %rxx``48 c7 [Mod+Reg+R/M](1 byte) [imm](4 byte)``Mod` 指定寄存器模式(2 位,`11` 为寄存器寻址),`Reg` 源操作数(3 位,这里是立即数,所以置为 0),`R/M` 指定目标寄存器(3 位);
191+
> - 跳转到 `rxx` 寄存器指向的地址`jmp *%rxx``ff [Mod+Reg+R/M](1 byte)``Mod` 指定寄存器模式(2 位,`11` 为寄存器寻址),`Reg` 设为 `100` 指定此指令为 `jmp` 指令(3 位,此处代表指令模式选择值),`R/M` 指定目标寄存器(3 位);
192+
>
193+
> <s>善用人工智能工具编写汇编/机器代码(逃</s>
188194

189195
## 三、栈溢出的防御
190196

@@ -207,16 +213,22 @@ gcc -fno-pie -no-pie -o dark-calc-my dark-calc.c
207213

208214
**Problem 3.1** 这种防御机制是否能够彻底“防御”栈溢出漏洞?
209215

210-
**Problem 3.2** 尝试查询资料,复原出之前实验中 `dark-calc` 的编译指令(有一个关键参数)。
216+
**Problem 3.2** 尝试查询资料,复原出之前实验中 `dark-calc` 的编译指令。
217+
218+
> [!tip]
219+
> 通过特定命令行参数,可以不开启防御机制;
220+
> 为触发可写栈空间漏洞,编译时包含了额外文件,不仅包含 `dark-calc.c`
211221
212-
## 四、栈帧的更多应用(协程)
222+
## 四、栈帧的更多应用——协程
213223

214224
> 改编自 2024 StackLab 的第三部分(源于 2022 年 Kieray Lab),减少协程部分的代码工作量,替换为阅读思考题。
215225
216226
在前两项任务中,我们已经加深了对程序运行时的栈帧的认识,接下来,我们使用栈帧和一定的汇编语言,来给 C 语言实现一些更加现代的功能吧。
217227

218228
在本实验中,你可以在 coroutine 文件夹下,使用 `make clean && make` 来编译代码,使用 `./program` 来运行代码。
219229

230+
**注意**:本实验中,各测试**默认注释掉**,需手动删除 `main.cpp` 中的注释符号来开启;提交时,请留下你能通过的测试,**将其他测试注释掉**
231+
220232
### 前言
221233

222234
> 在本实验中,你并不需要清晰地知道进程和线程的区别。你可以理解为一个进程可以开启多个线程,让 CPU 的不同核心同时计算不同的功能。
@@ -333,7 +345,7 @@ funcB:
333345

334346
接下来,我们将通过汇编实现 save 和 restore 的功能(显然,不考虑汇编和调用库函数的纯 C 语言难以实现这个功能),不过在此之前我们先着手用汇编语言写一个简单的函数吧。
335347

336-
**Problem 4.5** 请用不超过5条汇编指令<sup>2</sup>实现函数 naive_func,直接写在实验报告中。该函数的功能为:将函数的返回地址保存到第一个参数所指定的内存地址,然后返回0。可参考伪代码:
348+
**Problem 4.5** 请用不超过5条汇编指令<sup>2</sup>实现函数 naive_func,直接写在实验报告中。该函数的功能为:将函数的返回地址保存到第一个参数所指定的内存地址,然后返回 0。可参考伪代码:
337349

338350
```pseudocode
339351
naive_func(void **p):
@@ -427,8 +439,9 @@ catch{
427439
**Task 4.2** 请在 `context.c` 中实现函数 `__err_stk_push``__err_stk_pop`。如果一切顺利,在完成本节内容后,你将能通过 test3、test4 和 test5。
428440

429441
> [!tip]
442+
>
430443
> C 语言提供了 cleanup 属性,被添加该属性的变量会在生命周期结束时执行对应的函数,可以理解为类似析构函数的功能,可参考[这里](https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html)。我们实现了 `__err_cleanup` 函数作为对 `__err_try` 执行清理操作的接口。
431-
> 可以阅读 `context.h` 中对 `try``catch``throw` 的宏定义,帮助理解错误处理栈的应用场景,为接下来的 Task 4.3 做准备。
444+
> **可以阅读 `context.h` 中对 `try``catch``throw` 的宏定义**,帮助理解错误处理栈的应用场景,为接下来的 Task 4.3 做准备。
432445
433446
### 基于上下文切换的生成器(协程)
434447

@@ -495,9 +508,11 @@ funcB:
495508

496509
在已给出的 `__generator` 结构体中,我们使用 `data` 变量来传输 `yield``send` 的参数。为方便起见,我们将程序的主函数也视作一个generator(只不过你不需要手动给这个generator制作一个栈空间和初始上下文),因此全局变量 `__now_gen` 被初始化为了 `&__main_gen`,且每个generator都有自己的异常处理栈。
497510

498-
**Task 4.3** 请实现 `generator` 构造函数;完成此内容后,你将能通过 test6、test7 和 test8。
511+
**Task 4.3** 根据自己编写的上下文保存和恢复规则,参照样例解释,实现 `generator` 构造函数的**上下文构建部分**;完成此内容后,你将能通过 test6、test7 和 test8。
512+
513+
**Problem 4.3** 请阅读 `context.c` 中的函数 `send``yield``back_to_reality``context.h` 中对 `try``catch``throw` 的宏定义,并在报告中解释这些函数和宏定义的功能。
499514

500-
**Problem 4.3** 请阅读 `context.c` 中的函数 `send`, `yield``context.h` 中对 `try``catch``throw` 的宏定义,并在报告中解释这些函数和宏定义的实现细节
515+
**Problem 4.4** 任选 test6、test7、test8 中的一个,使用 GDB 动态调试,运用 `x/10gx $rsp` 指令查看至少两种不同位置处开始的栈帧或是伪栈帧并截图,体会协程中开辟虚拟栈空间的设计
501516

502517
### 协程的一些实际应用
503518

0 commit comments

Comments
 (0)