资讯详情

计算机系统基础实验全解析:从位运算、二进制拆弹到缓冲区溢出

📅 2026/10/20 18:20:19 | 华诺云谱 👁 阅读
计算机系统基础实验全解析:从位运算、二进制拆弹到缓冲区溢出
简介这份华中科技大学计算机系统基础实验报告面向正在学习计算机系统课程的高校学生与自学者围绕数据表示、位操作、补码运算、浮点数操作、Binary Bombs 与缓冲区溢出攻击等核心实验展开帮助读者在 Linux 环境下用 C 语言完成 bits.c 中的函数实现并理解二进制编码与底层运算原理。资源包共 1 个 docx 文件约 1.6MB内容为完整的课程实验报告涵盖实验概述、实验内容、实验设计与源码分析并附有实验总结便于对照学习。报告详细记录了 lsbZero、byteNot、byteXor、logicalAnd、rotateLeft、parityCheck 等函数的实现思路与合法操作符限制还包含 Binary Bombs 拆解和缓冲区溢出攻击的分析过程。目前已有 1387 人学习下载适合需要完成同类实验、巩固计算机系统底层知识或准备相关课程考核的读者参考。1. 计算机系统基础实验从数据表示到二进制拆弹的知识地图计算机系统基础这门课实验部分才是真正把“机器怎么想问题”刻进脑子里的地方。整份实验报告围绕三条主线展开数据在机器里的二进制表示、程序的机器级表示与逆向分析、以及由此引出的缓冲区溢出攻击原理。三条线不是并列关系而是层层递进——先搞懂整数和浮点数在 32 位里怎么摆再去看汇编代码怎么操作这些数据最后理解当程序不检查边界时会发生什么。适合正在系统学这门课、准备实验验收或者期末复习的人。梳理下来会发现真正拉开差距的不是会不会写代码而是能不能把位运算、栈帧结构、寄存器约定这些底层机制串成一条线。最容易忽略的是每个实验都在逼你用机器的视角看问题而不是用人的直觉。2. 数据表示实验位运算、补码与浮点数的硬核拆解2.1 位操作类函数的核心套路数据表示实验要求在 bits.c 中用有限的操作符实现指定功能操作符只允许! ~ ^ | 而且每个函数有最大操作数限制。这类题目的通用思路是先把目标功能翻译成“对哪些位做什么操作”再想办法用合法操作符拼出来。以lsbZero为例功能是把最低有效位置 0。最直觉的做法是x ~1但操作数可能超限。报告里用的方法是先右移一位再左移一位int lsbZero(int x) { x x 1; // 最低位被移出高位补符号位 x x 1; // 最低位补 0 return x; }这里有个容易翻车的点如果 x 是负数右移是算术右移高位补的是符号位 1再左移回来最低位确实是 0但高位不会丢——因为左移会把原来右移补进来的符号位再挤出去。所以这个写法对正负数都成立。但如果是逻辑右移的语言结果就不一样了。C 语言中右移有符号数是实现定义的行为大多数平台是算术右移这也是这个解法能成立的前提。byteNot要求对第 n 个字节取反。思路是构造一个“第 n 个字节全 1、其余全 0”的掩码然后与 x 异或int byteNot(int x, int n) { int y 0xff; n n 3; // n 乘以 8得到字节偏移的位数 y y n; // 掩码移到第 n 个字节位置 x x ^ y; // 异或实现按位取反 return x; }参数说明n 取值 0 到 30 表示最低字节LSB3 表示最高字节MSB。n 3等价于 n 乘以 8因为一个字节 8 位。掩码0xff左移 n 位后只有目标字节位置是 1异或后该字节每位翻转。byteXor比较两个数第 n 个字节是否相同。做法是把两个数都右移到目标字节在最低位然后用0xff取出异或后取逻辑非非int byteXor(int x, int y, int n) { n n 3; x x n; y y n; x x 0xff; y y 0xff; return !!(x ^ y); }!!的作用是把任意非零值变成 1零变成 0。异或结果非零说明两个字节不同返回 1为零说明相同返回 0。logicalAnd和logicalOr模拟逻辑运算符。关键是把操作数先归一化为 0 或 1再做位运算int logicalAnd(int x, int y) { x (!(!x)) (!(!y)); return x; }!x把非零变 0、零变 1再!一次就得到逻辑值。两个逻辑值相与结果就是逻辑与。rotateLeft是循环左移把高 n 位移到低位。报告里的实现是int rotateLeft(int x, int n) { int z; z ~(((1 31) 31) n); x ((x (32 (~n 1))) z) (x n); return x; }(1 31) 31构造出全 1 的掩码左移 n 位后取反得到低 n 位为 1、高位为 0 的 z。32 (~n 1)就是32 - n用补码运算避免直接用减号。x 右移32-n位把低 n 位移到高位与 z 相与保留再加上 x 左移 n 位的结果完成循环移位。parityCheck判断 1 的个数奇偶。报告用了折叠法每次把高半部分与低半部分异或逐步把 32 位折叠到 1 位int parityCheck(int x) { int y; y x 16; y y ^ x; y y ^ (y 8); y y ^ (y 4); y y ^ (y 2); y y ^ (y 1); y y 31; return !(!y); }每一步都把信息往高位集中最后第 31 位就是所有位的异或结果即奇偶校验位。2.2 补码运算与溢出判断mul2OK判断2*x是否溢出。溢出的条件是第 31 位和第 30 位不同——因为乘以 2 相当于左移一位如果最高两位不同左移后符号位会变int mul2OK(int x) { int m; m ((x 31) 0x1) ^ ((x 30) 0x1); return m ^ 0x1; }m为 1 表示溢出m ^ 1取反后返回溢出返回 0不溢出返回 1。mult3div2计算x*3/2向零舍入。先算x*3再右移一位。但右移是向下取整对于负数需要调整int mult3div2(int x) { int y (x 1) x; y (y 1) (((y 31) 1) (((y 31) 31) 1)); return y; }(y 31) 1取符号位((y 31) 31) 1取最低位。两个都为 1 时加 1实现向零舍入。subOK判断x-y是否溢出。减法溢出条件是x 和 y 符号不同且 x 与结果符号不同int subOK(int x, int y) { int m (x 31) 1; int n (y 31) 1; x (m ^ n) (m ^ (((x (~y 1)) 31) 1)); return (!x); }~y 1是 y 的补码取负x (~y1)就是 x-y。absVal取绝对值。正数返回自身负数返回补码取负int absVal(int x) { int y x 31; x (y (~x 1)) ((~y) x); return x; }y是符号位扩展正数时 y0负数时 y-1全 1。y (~x1)在负数时取补码~y x在正数时取原值。2.3 浮点数操作的位级实现float_abs返回浮点数的绝对值。IEEE 754 单精度中最高位是符号位其余是数值部分。绝对值就是把符号位清零unsigned float_abs(unsigned uf) { int x uf (~(1 31)); if (x 0x7f800000) { return uf; // NaN 返回原值 } else return x; }0x7f800000是指数全 1、尾数全 0 的表示即无穷大。大于这个值说明是 NaN按题目要求返回原参数。float_f2i把浮点数转成整数。需要提取符号、指数、尾数根据指数判断是否在 int 范围内int float_f2i(unsigned uf) { unsigned num 0x80000000; int x (uf 0x007fffff) ^ 0x00800000; // 补上隐含的 1 int order (uf 0x7f800000) 23; // 提取指数 if (order 158) return num; // 超出 int 范围 if (order 127) return 0; // 小于 1 else if (((uf 31) 1) 1) { // 负数 if (order 150) return ~(x (order - 150)) 1; else return ~(x (150 - order)) 1; } else { // 正数 if (order 150) return x (order - 150); else return x (150 - order); } }指数偏移是 127order - 127是实际指数。order 158对应实际指数大于 31超出 int 范围。order 127对应实际指数小于 0即绝对值小于 1返回 0。150 对应实际指数 23即小数点位置在尾数之后。3. Binary Bombs六个阶段的逆向拆解与调试策略3.1 拆弹的基本流程与工具链Binary Bombs 是一个 Linux 可执行程序包含 6 个阶段每阶段要求输入特定字符串错误则打印 BOOM!!! 并终止。目标是用 gdb 和 objdump 分析汇编代码推断出每阶段的正确输入。基本流程是先用objdump -d bomb asm.txt把反汇编代码导出然后在 asm.txt 中定位各 phase 函数。调试时用 gdb 在explode_bomb函数下断点这样一旦输入错误就会停在爆炸前可以回溯检查。常用命令包括x/s 地址查看字符串、x/d 地址查看十进制数、info registers查看寄存器、stepi单步执行汇编指令。阶段 1 到阶段 6 分别考察字符串比较、循环、条件分支、递归与栈、指针、链表与结构。难度递增但核心方法一致找到关键比较指令反推输入应该是什么。3.2 各阶段的关键汇编模式与破解思路阶段 1 是字符串比较。在 phase_1 中会调用strings_not_equal比较用户输入和程序中存储的字符串。关键是在调用前找到存储字符串的地址。报告中的做法是在 main 函数中追踪read_line的返回值确定用户输入存在%eax然后找到比较时另一个操作数的地址0x804a204用 gdb 查看该地址内容得到 The future will be better tomorrow.。阶段 2 是循环。phase_2 调用read_six_numbers要求输入 6 个数字。汇编中先检查第一个数是否为 0第二个数是否为 1然后进入循环每次检查当前数是否等于前两个数之和。所以序列是斐波那契式的0, 1, 1, 2, 3, 5。阶段 3 是条件分支。phase_3 的汇编中有一个跳转表根据输入的第一个数跳转到不同分支。报告中选择第一个数为 0对应分支要求第二个字符 ASCII 码为 0x76即 v第三个数为 0x104即 260。输入格式是数字、字符、数字。阶段 4 是递归调用和栈。phase_4 调用sscanf读取两个整数然后调用一个递归函数func4。需要分析 func4 的递归逻辑确定输入应该满足什么条件才能返回预期值。通常需要跟踪递归的基准情况和递推关系。阶段 5 是指针。phase_5 要求输入一个字符串程序对每个字符取低 4 位作为索引在一个字符数组中查找对应字符拼成新字符串与目标比较。需要反推出目标字符串对应的索引序列再映射回输入字符。阶段 6 是链表和结构。phase_6 要求输入 6 个数字程序根据这些数字重新排列一个链表然后检查链表是否按降序排列。需要先查看链表中各节点的值再确定输入顺序。隐藏阶段在阶段 4 的解之后附加特定字符串才会出现通常涉及更复杂的条件判断。3.3 调试中的常见翻车点最容易翻车的地方是地址看错。汇编中lea和mov的区别很关键lea加载的是地址mov加载的是地址里的值。在 gdb 中用x/s查看字符串时要确认地址确实是字符串首地址而不是指向指针的指针。另一个坑是大小端。x86 是小端序用x/d查看多字节整数时gdb 会自动按小端解释但如果手动从内存中读字节拼数顺序容易搞反。还有符号扩展问题。汇编中movsbl和movzbl分别做符号扩展和零扩展影响后续比较结果。在分析字符比较时如果源寄存器高位有残留数据可能导致比较失败。4. 缓冲区溢出攻击从栈帧结构到返回地址覆盖4.1 栈帧布局与函数调用约定缓冲区溢出攻击实验建立在 Binary Bombs 对汇编和栈的理解之上。x86 函数调用时call指令先把返回地址压栈然后被调函数执行push %ebp; mov %esp, %ebp建立新栈帧。局部变量在%ebp下方分配参数在%ebp上方。典型栈帧从高地址到低地址依次是调用者的栈帧、参数、返回地址、旧的%ebp、局部变量、缓冲区。如果缓冲区写越界就会覆盖旧的%ebp和返回地址。攻击的目标就是精心构造输入让返回地址指向注入的代码或已有函数。4.2 攻击构造的关键步骤构造攻击字符串需要确定几个关键偏移缓冲区起始地址到返回地址的距离、目标地址的值、以及需要填入的填充字节。通常用 gdb 在函数返回前查看%ebp和%esp计算偏移。如果实验允许注入 shellcode需要把 shellcode 放在缓冲区开头然后把返回地址覆盖为缓冲区起始地址。如果实验要求调用已有函数如touch函数则把返回地址覆盖为该函数地址。报告中提到的缓冲区溢出攻击实验核心是理解“程序不检查输入长度”这个漏洞的本质。防御手段包括栈保护canary、不可执行栈NX、地址空间随机化ASLR这些机制在后续课程中会详细展开。4.3 实验环境与工具实验在 Linux 环境下进行用 gcc 编译时通常需要关闭栈保护gcc -fno-stack-protector -z execstack -o target target.c。-fno-stack-protector关闭 canary-z execstack允许栈上执行代码。用 gdb 调试时可以在strcpy等危险函数处下断点观察缓冲区溢出时的内存变化。5. 易漏点与易错点那些实验报告里不会明说的坑5.1 位运算实验的隐藏约束易漏点操作符数量限制。每个函数都有 Max ops 限制写的时候容易只顾功能正确忘了数操作符。补法写完先数一遍!、~、、^、|、、、各算一个赋值不算。如果超了想办法合并或换思路。易漏点常量范围限制。bits.c 中通常规定常量只能在 0 到 255 之间。0xffffffff这种大常量不能直接用需要用~0或移位构造。补法养成用~0、(131)31等惯用法的习惯。易漏点有符号右移的实现定义行为。C 标准没有规定有符号数右移是算术还是逻辑但大多数平台是算术右移。如果代码依赖这个行为换平台可能翻车。补法在实验报告中注明平台假设或者用无符号数显式处理。5.2 拆弹实验的调试陷阱易漏点断点设错位置。在explode_bomb下断点是对的但如果程序在到达断点前就崩溃了说明输入格式不对比如该输入数字的地方输入了字符串。补法先在read_line或sscanf后下断点确认输入被正确解析。易漏点忽略隐藏阶段。隐藏阶段只有在阶段 4 的解之后附加特定字符串才会触发很多人拆完 6 个阶段就以为结束了。补法仔细看实验说明确认是否有隐藏阶段通常需要分析阶段 4 的汇编找到触发条件。易漏点gdb 中地址随机化。如果系统开启了 ASLR每次运行地址可能不同导致硬编码的地址失效。补法在 gdb 中可以用set disable-randomization on关闭 ASLR或者用相对地址计算。5.3 缓冲区溢出实验的常见错误易漏点偏移计算错误。缓冲区到返回地址的偏移不是固定的取决于编译器版本、优化级别和栈对齐。补法用 gdb 实际查看%ebp和缓冲区地址计算差值不要凭感觉猜。易漏点shellcode 中有空字节。strcpy遇到\0会停止复制如果 shellcode 中间有空字节后面的内容不会被复制到栈上。补法选用不含空字节的 shellcode或者用其他输入函数。易漏点栈不可执行。现代系统默认栈不可执行即使覆盖了返回地址跳到栈上也会段错误。补法编译时加-z execstack或者改用 ret2libc 等不需要执行栈上代码的方法。6. 复习清单从知识地图到查漏补缺的实操路径复习这门课的实验部分建议按以下顺序过一遍。先拿一张白纸画出三条主线的知识地图数据表示整数补码、浮点 IEEE 754、位运算、机器级表示汇编、栈帧、寄存器约定、安全缓冲区溢出、防御机制。画不出来的地方就是漏洞。然后逐个实验过关键函数。数据表示实验重点看rotateLeft、parityCheck、float_f2i这三个它们分别代表位操作、折叠法和浮点转换的典型思路。拆弹实验重点看阶段 4 和阶段 6递归和链表是难点。缓冲区溢出重点看栈帧布局和偏移计算。下面这张表可以用来快速自查知识模块核心要点自查问题整数表示补码、符号扩展、溢出判断能写出subOK的溢出条件吗浮点表示IEEE 754、指数偏移、隐含 1能解释float_f2i中 150 的含义吗位运算掩码构造、折叠法、循环移位能不用减号实现32-n吗汇编与栈栈帧布局、调用约定、gdb 调试能画出函数调用时的栈变化吗逆向分析跳转表、递归、链表能还原阶段 6 的链表顺序吗缓冲区溢出返回地址覆盖、shellcode、防御能计算缓冲区到返回地址的偏移吗查漏补缺的方法是对每个自查问题如果能流畅回答并写出关键代码或汇编片段就算过关如果卡壳回到对应章节重新梳理。实验报告里的源码和汇编代码是最好的复习材料不要只看结论要自己推一遍。提示复习时把 gdb 和 objdump 放在手边遇到不确定的汇编指令直接查比死记硬背高效得多。从那以后我每次复习这门课都强制自己先默写栈帧结构图再默写 IEEE 754 的位布局写不出来就翻实验报告重新推。这两个图能默出来大部分实验题都能找到切入点。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑