Lab1 Xv6 && system calls

Using gdb 平时我们用的调试工具其实都是图形化后的gdb,使用起来非常的方便,但是熟悉原生的gdb会使我们的效率进一步提升。我认为学习好用gdb调试是一项非常重要的技能。 编译并启动 ==make qemu-gdb== 使用上述命令编译项目并且直接以调试模式启动。但是这个项目启动的其实是在本地的一个远程gdb,通过另一个窗口进行调试。 下面有几个关于系统调用的小问题 Looking at the backtrace output, which function called syscall? 显然是usertrap() What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.) 通过提示找到trapframe的地址,在kernel/proc.h中找到a7寄存器对应的偏移地址。 把168换算成16进制 尝试着打印0x87f560a8 这个就是寄存器a7的值。其代表的具体含义通过所给的提示到对应文件中查找之后也是十分的明了。 应该就是系统的调用号。 What was the previous mode that the CPU was in? spp位表示其在什么状态。 通过图示得出,spp在二进制的第8bit. 由图可知8bit是0,所以之前是用户状态 。 System call tracing Attack xv6 这个task主要是利用xv6故意留下的bug然后获取到销毁内存但是保留了脏页的数据。然后通过一些比较hack的手段把字段给找出来。 page组成 上图是一个简化的逻辑页表,下一个实验也会用到,这里理顺一下逻辑有利于分析代码的组成。 上图是地址转化的细节。 页表项(PTE)包含标志位,告诉硬件应该如何使用这些虚拟地址。 这些相关位的定义都在(kernel/riscv.h)中 ...

February 25, 2025 · 1 min · Theme PaperMod

变量

变量的绑定与解构 手动设置可变性 在rust语言中可以手动设置变量的可变性。这是灵活性和安全性的结合。但是这样肯定会在编码的过程中付出更多的思考,但这也是权衡之后做出的选择。 变量绑定 在rust语言中变量和标识符之间是一种绑定关系,直接是所有权的改变。 简单来说任何一块有意义的内存都有所属,而且这个关系是唯一的。 这样就以为这变量和内存之间只能真诚的进行1v1了。 变量可变性 rust语言的变量在默认情况下是不可变的,但是可以通过mut关键字让变量变为可变的。 下面我们进行编码进行实践操作 fn main() { let x = 5; println!("The value of x is: {}", x); x = 6; println!("The value of x is: {}", x); } 我们可以预想的到这样的代码会报错 由于我们对x发生了两次赋值。我们遵循报错给出的建议,使用mut对变量进行修饰。 这样由于显示的规定变量是否可以被修改,在多线程的编程过程中能让我们少死很多脑细胞,对程序员也是一种保护。 let mut x=5; println!("the value of x is:{}",x); x=6; println!("the value of x is:{}",x); 这次非常的顺利运行结果没有任何问题。 使用下划线忽略未使用的变量 在日常编程的过程中经常会发出“变量从未使用”的警告,如果此时你正在设计一个项目,你可能会拥有很多的未使用的变量。此时你可以使用下划线作为变量的开头,屏蔽警告。 fn main() { let _x = 5; let y = 10; } 这样的话就不会对未使用的变形进行警告了。 ...

February 3, 2025 · 1 min · Theme PaperMod

虚拟化--进程管理API:fork, execve, exit

操作系统上的进程 背景回顾:有关状态机、并发和中断的讨论给我们真正理解操作系统奠定了基础,现在我们正式进入操作系统和应用程序的 “边界” 了。让我们把视角回到单线程应用程序,即 “执行计算指令和系统调用指令的状态机”,开始对操作系统和进程的讨论。 本讲内容:操作系统上的进程 操作系统上的第一个进程 UNIX/Linux 进程管理 API: fork, execve, exit fork() 理解 fork(): fork() 会完整复制状态机;新创建的状态机返回值为 0,执行 fork() 的进程会返回子进程的进程号。同时,操作系统中的进程是并行执行的。程序的精确行为并不显然——model checker 可以帮助我们理解它。 在这个例子中,我们还发现执行 ./a.out 打印的行数和 ./a.out | wc -l 得到的行数不同。根据 “机器永远是对的” 的原则,我们可以通过提出假设 (libc 缓冲区影响) 求证、对比 strace 系统调用序列等方式,最终理解背后的原因。标准输入输出的缓冲控制可以通过 setbuf(3) 和 stdbuf(1) 实现。 execve() exit()

February 2, 2025 · 1 min · Theme PaperMod

Project 4 - 授权码管理与 PKCE

实验目标 本周我们将实现 OAuth 2.0 的核心:授权码流程。完成本实验后,你将能够: ✅ 理解授权码在 OAuth 2.0 流程中的关键作用 ✅ 实现授权码的创建和一次性使用验证 ✅ 理解授权码被截获的安全威胁 ✅ 实现 PKCE(Proof Key for Code Exchange)安全增强 ✅ 掌握 SHA-256 哈希和 Base64 URL 编码 理论背景 1. 授权码流程详解 1.1 为什么需要授权码? OAuth 2.0 使用两步验证而非直接颁发访问令牌,主要出于安全考虑: ❌ 不安全的单步流程(隐式授权): 用户登录 → SSO 直接返回访问令牌到浏览器 问题:访问令牌暴露在浏览器历史记录、日志中 ✅ 安全的两步流程(授权码): 步骤 1:用户登录 → SSO 返回临时授权码到浏览器 步骤 2:客户端后端用授权码 + 密钥换取访问令牌 优势:访问令牌只在后端传输,永不经过浏览器 1.2 授权码的生命周期 时间轴: T0: 用户完成登录,SSO 生成授权码 授权码特性: - 随机生成(UUID 或密码学随机数) - 生命周期极短(5 分钟) - 一次性使用(use once and destroy) T0+30s: 客户端用授权码换取令牌 授权码被立即销毁 T0+5min: 授权码自动过期 (即使未被使用) 2. 授权码截获攻击与 PKCE 2.1 授权码截获攻击场景 攻击场景:恶意应用截获授权码 正常流程: 1. 合法 App 发起授权请求 2. 用户登录,SSO 重定向到 callback URL 3. 恶意 App 拦截重定向(通过注册相同的 URL Scheme) 4. 恶意 App 获取授权码 5. 恶意 App 用授权码 + 客户端密钥换取令牌 ← 问题! 在移动应用中,客户端密钥无法安全存储 (反编译即可获取) 2.2 PKCE 工作原理 PKCE(读作 “pixy”)通过动态密钥解决此问题: ...

January 29, 2025 · 5 min · Theme PaperMod

Project 2 - 用户和客户端验证

实验目标 本周我们将实现 OAuth 2.0 授权流程的第一道防线:身份验证。完成本实验后,你将能够: ✅ 实现安全的用户密码验证(使用 BCrypt) ✅ 实现账户锁定机制(防止暴力破解) ✅ 实现密码过期策略(强制定期更换密码) ✅ 实现客户端应用验证(验证 client_id、client_secret 和 redirect_uri) ✅ 理解重定向 URI 验证的安全重要性 理论背景 1. 用户认证的安全原则 1.1 密码存储 绝对禁止:以明文或可逆加密方式存储密码 推荐方案:使用强单向哈希算法(BCrypt、Argon2、PBKDF2) BCrypt 优势: 内置盐值(Salt),每次哈希结果不同 计算成本可调(通过 work factor 参数) 抗彩虹表攻击 示例: // 生成密码哈希 String hashedPassword = passwordEncoder.encode("userPassword123"); // 输出类似:$2a$10$N9qo8uLOickgx2ZMRZoMye1J9rY7AVMZ8tPNLPZdRs5u5xP5c5fUa // 验证密码 boolean matches = passwordEncoder.matches("userPassword123", hashedPassword); // 返回 true 1.2 账户锁定策略 目的:防止暴力破解攻击 实现方案: 追踪每个用户的连续登录失败次数 达到阈值(如 5 次)后锁定账户一段时间(如 10 分钟) 成功登录后清除失败计数器 使用缓存的原因: 高性能:失败计数不需要持久化到数据库 自动过期:利用缓存的 TTL 特性实现锁定时间 降低数据库压力 1.3 密码过期策略 目的:降低长期使用同一密码的风险 ...

January 15, 2025 · 4 min · Theme PaperMod