news 2026/10/28 12:30:57

DOS实模式汇编密码校验:REPZ CMPSB字符串比较与缓冲区管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DOS实模式汇编密码校验:REPZ CMPSB字符串比较与缓冲区管理

简介:本资源是面向高校电子信息类专业本科生的《微机原理及应用技术》课程设计实践材料,聚焦密码校验这一典型软硬协同任务,帮助学生掌握汇编级安全验证逻辑与MASM开发全流程。压缩包为1个249KB的Word文档(.doc),完整包含河北科技大学电信091班课程设计报告全文:涵盖题目说明、详细设计要求(如8–16位混合字符校验、明文禁用与哈希思路)、进度与小组分工安排、MASM环境配置方法、核心算法实现步骤、带注释的完整汇编代码、程序界面截图及流程图、测试用例分析与课设总结反思等11个结构化章节。文档内容详实、排版规范,代码模块按“密码接收→长度判断→字符类型扫描→错误提示→退出”分节注释,便于理解底层指令(如LEA、CMP、JNE、CALL)的实际作用。目前已有652人学习下载,是微机原理课设中兼具教学性、可复现性与工程参考价值的高质量结题范本。

1. 微机原理课程设计密码校验:一个真实跑在 DOS 上的汇编黑匣子,它不依赖任何库、不调用 API、连字符串比较都靠 REPZ CMPSB 硬刚

你有没有试过,在 Windows 10 或 Win11 里双击一个.exe文件,结果弹出“此应用无法在你的电脑上运行”?但这个微机原理课设里的PASSWORD.EXE,恰恰是那种——必须关掉现代系统保护、开个纯 DOS 环境(比如 DOSBox)、甚至得手动配好MASM 5.0 + LINK 5.6才能亮起光标、显示方框菜单、让你敲1输入密码、敲2校验、敲3退出的“古董级”程序。它不是 Python 脚本,没有input()和if password == stored;它是实打实把BUF1和BUF2两个缓冲区地址送进SI/DI,用REPZ CMPSB逐字节比对,失败三次就JMP EXIT直接回 DOS 的硬核实现。这不是教学演示,是当年某高校电信 091 班四人小组在 2011 年底用纸笔画流程图、在 DOSBox 里反复DEBUG查寄存器、为一个0AH换行符多写两行INT 10H而熬通宵的真实项目。它适合所有正在啃《微型计算机原理及应用技术》教材、被INT 21H功能号背得头晕、想亲手把“内存段地址/偏移地址/堆栈平衡”这些抽象概念砸进.asm文件里跑起来的初学者;也适合那些想逆向拆解一个 DOS 实模式下完整交互逻辑、看懂“为什么菜单要手绘 ASCII 框线”“为什么输入缓冲区必须定义成DB 255,0”的熟手。它不炫技,但每行汇编都在回答一个问题:CPU 是怎么从键盘读一个字符,又怎么把它变成屏幕上跳动的PASSWORD ERROR!。

2. 从 DOS 实模式到主菜单:环境搭建、代码结构与核心数据段布局

2.1 为什么非得用 MASM 5.0?DOS 实模式下的段寻址铁律

这个课设绝不能用 Visual Studio 或 VS Code 直接编译——它运行在 16 位实模式下,CS:IP 指向代码段,DS 指向数据段,SS:SP 指向堆栈段,三者必须显式初始化且互不重叠。MASM 5.0 是那个年代最稳定、文档最全、与 DOSBox 兼容性最好的工具链。它生成的.obj文件经LINK连接后产出.exe,格式为 MZ(即 DOS 可执行头),能被 DOS 内核原生加载。新版 MASM(如 10.0)默认生成 32 位 PE 文件,根本进不了 DOS 环境。所以第一步不是写代码,而是确认你的 DOSBox 配置里挂载了包含ML.EXE(MASM 编译器)、LINK.EXE(连接器)、DEBUG.EXE(调试器)的路径。常见错误是LINK报错no stack segment,原因就是.asm文件里没写STACK SEGMENT PARA STACK 'STACK'—— 实模式下堆栈段是强制要求,哪怕只放 128 字节也要明确定义。

2.2 数据段(DS)的生死线:缓冲区定义与 DOS 输入规范

看附录代码里这三行:

MSG1 DB 255,0 BUF1 DB 255 DUP(0) MSG2 DB 255,0

这不是随便写的。DOS 功能号0AH(带缓冲区的字符串输入)有严格格式:第一个字节存最大可输入长度(255),第二个字节由 DOS 填入实际输入长度,后续字节才是字符。所以MSG1必须是连续的 255+1 字节空间,BUF1则是纯字符存储区。若写成MSG1 DB 10,0,用户最多输 10 字符,但BUF1若只定义10 DUP(0),当用户输满 10 个时,DOS 会往BUF1+10写结束符0DH(回车),导致越界覆盖相邻变量——这是课设调试阶段最常翻车的点。同理,MENU字符串末尾必须加$,因为INT 21H/09H显示字符串时,以$为终止符,不是\0。漏写$,屏幕会乱码刷屏直到撞上内存里某个偶然的$。

2.3 代码段(CS)的控制流骨架:主循环与功能跳转逻辑

整个程序的灵魂是AGAIN:标号开始的无限循环:

AGAIN: MOV AX,3 INT 10H ; 清屏并设文本模式 LEA DX,MENU MOV AH,9 INT 21H ; 显示主菜单 INPUT: MOV AH,0 INT 16H ; 读键盘扫描码,AL=ASCII CMP AL,'1' JNZ CP2 CALL LP1 ; 输入密码 JMP AGAIN ; 返回菜单 CP2: CMP AL,'2' JNZ CP3 CALL LP2 ; 校验密码 JMP AGAIN CP3: CMP AL,'3' JNZ INPUT ; 不是3则重新读键 JMP EXIT ; 是3则退出

注意INT 16H/00H读的是扫描码+ASCII 码,AL直接得字符,所以比较用'1'(ASCII 31H)而非数字 1。JMP AGAIN是关键——它让程序永远回到清屏→显示菜单→等待输入的状态,形成真正的“交互式”体验。而CALL后必须RET,否则LP1执行完会继续执行LP2的指令,造成逻辑崩坏。这里没有main()函数概念,只有标号和跳转,是实模式编程最原始的控制方式。

2.4 ASCII 框线绘制:用字符码拼出 DOS 时代的 UI

主菜单里的方框不是画出来的,是用 IBM 扩展 ASCII 码“拼”出来的:

DB 25 DUP(' '),201,21 DUP(205),187,0DH,0AH ; 25空格 + 左上角(201) + 21横线(205) + 右上角(187) + 回车换行 DB 25 DUP(' '),186,' 1.INPUT THE PASSWORD',186,0DH,0AH ; 25空格 + 竖线(186) + 文字 + 竖线(186) + 回车换行

201(╔)、187(╗)、186(║)、200(╚)、188(╝)、205(═)是 DOS 下标准的框线字符。25 DUP(' ')保证文字居中,0DH,0AH是 DOS 换行符(回车+换行)。少一个0AH,下一行会挤在当前行末;多一个0DH,会空出额外行距。这种 UI 构建方式,是理解“字符终端”本质的绝佳入口——没有像素坐标,只有行列位置和字符映射。

3. 密码输入与校验:缓冲区管理、字符串比较与三次失败机制

3.1 输入密码(过程一 LP1):清屏、提示、接收、回车处理

LP1的任务是让用户输入初始密码并存入BUF1:

LP1: MOV AX,3 ; 设文本模式,清屏 INT 10H LEA DX,TIPS ; 提示语 "INPUT A STRING: $" MOV AH,9 INT 21H LEA DX,MSG1 ; 指向输入缓冲区首地址 MOV AH,0AH ; DOS 输入功能 INT 21H ; 用户输入,DOS 自动填 MSG1+1=长度,MSG1+2 开始存字符 MOV AX,0E0DH ; 光标下移一行:AH=0EH(teletype 模式),AL=0DH(回车) INT 10H MOV AL,0AH ; 再输出换行符 0AH(换行) INT 10H RET

关键点在于MOV AX,0E0DH和MOV AL,0AH这两行。DOS 功能0AH输入后,光标停在输入字符串末尾,不自动换行。若直接JMP AGAIN,下次菜单会显示在密码后面,视觉混乱。所以必须用INT 10H/0EH输出回车(让光标回到行首),再用INT 10H/0AH换行(光标下移)。这是 DOS 文本模式下光标控制的固定套路,漏掉任一环节,界面就“粘连”在一起。

3.2 密码校验(过程二 LP2):长度预检、逐字节比对与失败计数

校验逻辑藏在LP2中,其健壮性远超表面看起来的简单:

LP2: MOV AX,3 INT 10H CLD ; 清方向标志,确保 SI/DI 正向递增 PUSH DS ; 保存 DS,因 REPZ CMPSB 会改 ES POP ES ; 将 ES 设为与 DS 同值,使 CMPSB 能跨段比较 MOV BP,3 ; 初始化失败计数器为3 SSS: LEA DX,PASS ; 显示 "ENTER YOUR PASSWORD: $" MOV AH,9 INT 21H LEA DX,MSG2 ; 指向校验输入缓冲区 MOV AH,0AH INT 21H ; 用户输入待校验密码 LEA SI,BUF1 ; SI -> 存储的密码首地址 LEA DI,BUF2 ; DI -> 待校验密码首地址 MOV CH,0 ; 清 CX 高字节 MOV CL,MSG1+1 ; CX = 存储密码长度(DOS 写入 MSG1+1) CMP CL,MSG2+1 ; 比较两密码长度 JNZ WRONG ; 长度不同直接错 REPZ CMPSB ; CX 次,SI/DI 指向字节相等则继续,否则 ZF=0 JZ RIGHT ; ZF=1 表示全部相等 WRONG: LEA DX,NO ; 显示 "PASSWORD ERROR$" MOV AH,9 INT 21H DEC BP ; 失败计数减1 JNZ SSS ; BP!=0 继续循环 STC ; 设置进位标志,作为“彻底失败”信号 JMP OK RIGHT: LEA DX,YES ; 显示 "PASSWORD CORRECT$" MOV AH,9 INT 21H OK: MOV AH,1 ; 等待按键 INT 21H RET

这里CLD(Clear Direction Flag)至关重要:它确保REPZ CMPSB执行时SI和DI自动递增(+1),而不是递减。若忘记CLD,在某些 DOSBox 版本下会因方向标志为 1 导致SI/DI乱跳,比较永远失败。PUSH DS / POP ES是为了满足CMPSB要求——它比较DS:SI和ES:DI,而 DOS 输入后ES可能被污染,必须显式设为与DS一致。REPZ CMPSB是核心:它等价于while (CX > 0 && [DS:SI] == [ES:DI]) { SI++; DI++; CX--; },ZF=1 当且仅当 CX 减到 0 且所有字节相等。这才是“汇编级字符串比较”的真面目,不是高级语言的strcmp()黑盒。

3.3 三次失败机制:BP 寄存器的妙用与退出信号传递

失败计数用BP寄存器(Base Pointer)而非更常见的CX,是有意为之:CX被REPZ指令占用,BP在此场景下闲置,且DEC BP / JNZ SSS指令对紧凑。当BP减到 0,执行STC(Set Carry Flag),这是 x86 传统退出信号——主程序可通过检查进位标志决定是否彻底退出。虽然本课设中STC后直接JMP OK等待按键,但这种用标志位传递状态的设计,是嵌入式和底层开发的通用范式。若想扩展为“失败三次后锁定 30 秒”,只需在STC后插入延时循环,而非修改主逻辑。

4. 避坑:调试中踩过的五个血泪坑与现场排查指南

提示:以下问题均来自真实调试过程,非理论推演。每个现象都对应一次DEBUG单步执行时寄存器值的异常跳变。

4.1 现象:输入密码后,菜单显示错位,文字挤在屏幕右上角

原因:LP1结束后未执行MOV AX,3 / INT 10H清屏,且AGAIN循环开头的MOV AX,3 / INT 10H被意外跳过(如JMP AGAIN写成JMP INPUT)。DOS 文本模式下,光标位置是全局状态,不清屏会导致新内容叠加在旧内容上。
解决:在CALL LP1和CALL LP2返回后,JMP AGAIN前加断点,用DEBUG观察AH寄存器值。若AH != 3,说明模式未重置。强制在AGAIN标号后第一行写MOV AX,3 / INT 10H,并确保所有跳转都指向AGAIN,而非中间某行。

4.2 现象:输入密码时,按回车后程序卡死,DEBUG显示IP停在INT 21H指令处不动

原因:MSG1缓冲区定义错误,如写成MSG1 DB 10,0,10 DUP(0),但 DOS 功能0AH要求缓冲区首字节为最大长度,第二字节由 DOS 写入实际长度。若首字节为0(如误写DB 0,0),DOS 认为“不允许输入”,直接挂起。
解决:用DEBUG加载.exe后,-d ds:0 l 20查看数据段前 20 字节,确认MSG1地址处第一个字节为FF(255)或合理值(如0A)。若为00,立即修改源码并重编译。

4.3 现象:密码校验永远报错,即使输入完全相同的字符串

原因:REPZ CMPSB前未执行CLD,导致方向标志DF=1,SI/DI递减,比较从缓冲区末尾开始,而BUF1/BUF2末尾是0DH(回车符)或随机垃圾值。
解决:在REPZ CMPSB指令前加CLD,并在DEBUG中用-r flags查看DF位是否为0(nv表示 not vertical,即 DF=0)。若为1(dn),则必有CLD缺失。

4.4 现象:输入密码后,屏幕显示乱码,如PASSWORD CORRECT变成PASSW?R? CORRECT

原因:YES字符串定义末尾漏写$,INT 21H/09H持续输出直到遇到内存中某个偶然的$,而该位置恰好是其他变量(如NO字符串)的$,导致截断。
解决:检查所有DB ... '$'定义,用DEBUG的-d cs:offset YES l 20确认YES字符串结尾确实是24($的 ASCII 码)。若不是,修改源码并重编译。

4.5 现象:按3退出时,屏幕显示PRESS ANY KEY TO EXIT ...后,按任意键程序无响应,DOS 提示符不出现

原因:EXIT过程中MOV AH,4CH后缺少INT 21H,或INT 21H被注释掉。4CH是 DOS 退出功能号,必须配对INT 21H才生效。
解决:检查EXIT标号下是否有MOV AH,4CH / INT 21H。用DEBUG单步执行到INT 21H前,-r ah确认AH=4C;执行INT 21H后,程序应立即返回 DOS,DEBUG会退出。

5. 主菜单与 DOS 交互:从 ASCII 框线到系统退出的完整闭环

5.1 主菜单(MENU)的内存布局与 DOS 显示原理

MENU不是一张图片,而是一段精心排布的字节数组,其结构决定了 DOS 如何渲染:

偏移字节值含义作用
0-40AH,0AH,0AH,0AH,0AH5 个换行符把光标推到屏幕顶部留白
5-3531 DUP(' ') + 'MAIN MENU' + 0DH,0AH31 空格 + 文字 + 回车换行居中标题行
36-5825 DUP(' ') + 201 + 21 DUP(205) + 187 + 0DH,0AH左上角+横线+右上角+换行顶边框
59-8125 DUP(' ') + 186 + ' 1.INPUT...' + 186 + 0DH,0AH竖线+文字+竖线+换行第一选项行
............
最后'CHOICE(1,2,3):$'提示语+终止符用户输入引导

DOS 功能09H从DX指向的地址开始,逐字节读取,遇$停止。因此MENU必须是连续内存块,且$是唯一终止符。若中间某行漏写$,09H会一路读下去,把后续的MESG或PASS字符串也当菜单内容输出,造成严重错乱。这就是为什么MENU定义中,每一行末尾都明确写了0DH,0AH和$——它们共同构成 DOS 文本输出的语法。

5.2 退出流程(EXIT):从屏幕提示到 DOS 控制权移交

EXIT过程表面简单,实则涉及 DOS 系统调用的完整生命周期:

EXIT: LEA DX,MESG ; 加载提示字符串地址 MOV AH,9 ; 选择显示字符串功能 INT 21H ; DOS 执行显示,光标停在字符串末尾 MOV AH,1 ; 键盘输入功能,AL=按键ASCII INT 21H ; 等待用户按键,不回显 MOV AH,4CH ; DOS 退出功能号(带返回码) INT 21H ; 交还控制权给 DOS,程序终止

关键细节:MOV AH,1输入是阻塞的,用户不按键程序就卡住;INT 21H/4CH的返回码默认为AL值(此处未设AL,故为上次INT 21H/1的按键 ASCII),但 DOS 不关心此值,重点是4CH功能号触发内核清理。若省略MOV AH,4CH,INT 21H会执行上次的功能(如09H),导致无限显示MESG。这是初学者最易忽略的“功能号遗忘症”。

5.3 DOSBox 下的实操验证技巧:三步定位问题根源

在 DOSBox 中调试,别依赖 IDE,用原始DEBUG最有效:

  1. 第一步:确认入口点
    启动 DOSBox,挂载代码目录,输入debug password.exe。-r查看寄存器,CS:IP应指向START标号(通常为CS:0100)。若IP异常,说明.exe头损坏或链接错误。
  2. 第二步:单步跟踪主循环
    在AGAIN处设断点:-a 100(假设START在0100),输入jmp 0100跳转。然后-t单步,观察AX,DX,AH变化。当AH=9时,DX应指向MENU地址;当AH=0(INT 16H),AL应为按键 ASCII。
  3. 第三步:内存快照比对
    输入密码后,在LP2的REPZ CMPSB前,用-d ds:offset BUF1 l 10和-d ds:offset BUF2 l 10对比两个缓冲区内容。正常时BUF1和BUF2的字符部分应完全一致(0DH结束符除外),长度字节(MSG1+1,MSG2+1)也应相等。不等则问题出在输入环节。

6. 进阶技巧:如何把课设代码改造成可复用的 DOS 密码模块

6.1 模块化改造:分离数据段与代码段,支持外部调用

原课设将CODE SEGMENT和数据混写,不利于复用。改造目标是让密码校验逻辑成为独立子程序,可被其他 DOS 程序CALL。核心改动:

  • 将BUF1,BUF2,MSG1,MSG2等数据移至单独的DATA SEGMENT,并声明为PUBLIC;
  • 在CODE SEGMENT中,LP2改为PUBLIC CHECK_PASSWORD,入口参数通过寄存器传递:
    ; 调用约定:DS:SI -> 存储密码地址,ES:DI -> 待校验密码地址,CX -> 密码最大长度 CHECK_PASSWORD PROC FAR PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES ; ... 原 LP2 逻辑,但用传入的 SI/DI/CX 替代硬编码地址 POP ES POP DS POP DI POP SI POP DX POP CX POP BX POP AX RETF CHECK_PASSWORD ENDP

这样,其他程序只需设置好寄存器,CALL FAR PTR CHECK_PASSWORD即可复用校验逻辑,无需复制粘贴整段代码。

6.2 增强安全性:添加简单哈希(XOR 混淆)避免明文存储

课设中BUF1明文存密码,存在安全风险。可在输入后立即混淆:

; 在 LP1 结束前,对 BUF1 中的密码做 XOR 混淆 LEA SI,BUF1 MOV CL,MSG1+1 ; 密码长度 XOR CH,CH MOV BX,OFFSET KEY ; KEY DB 'SECRETSALT'(10字节密钥) XOR_LOOP: LODSB ; AL=[SI], SI++ XOR AL,[BX] STOSB ; [DI]=AL, DI++ INC BX CMP BX,OFFSET KEY+10 JB NEXT_KEY MOV BX,OFFSET KEY NEXT_KEY: LOOP XOR_LOOP

校验时,对BUF2做同样 XOR,再比较。虽非加密学强度,但能防止DEBUG直接d ds:offset BUF1看到明文,符合课设“增强安全性”的要求。

6.3 跨平台兼容:在 64 位 Windows 上运行的 DOSBox 配置要点

现代系统需正确配置 DOSBox 才能稳定运行:

  • dosbox.conf中关键项:
    [cpu] core=dynamic cputype=auto cycles=5000 [render] frameskip=0 aspect=true [autoexec] mount c c:\path\to\your\code c:
  • cycles=5000防止过快导致输入丢失;frameskip=0确保每帧渲染,避免菜单闪烁;mount命令必须指向包含ML.EXE,LINK.EXE,PASSWORD.ASM的目录。若编译时报error A2006: undefined symbol : xxxx,大概率是LINK未找到LIB文件,需在autoexec中加set lib=c:\masm\lib。

从那以后我每次写 DOS 汇编,都强制走一遍DEBUG单步:先-r看寄存器初始态,再-t走三步,-d查一眼内存,确认DS,ES,SI,DI全在预期位置。不是信不过自己写的代码,是信不过 CPU 在实模式下对段寄存器那毫秒级的微妙依赖。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 12:29:17

PHP异步HTTP客户端Ponytail:从安装到并发实战指南

最近好几个读者私信我同一个问题:网上突然多了不少讲 ponytail 插件、ponytail skill 的帖子,可到底怎么装、怎么用,越看越糊涂。我去翻了翻这些内容,发现大家说的 ponytail,十有八九指的是 PHP 生态里的异步 HTTP 客户…

作者头像 李华
网站建设 2026/10/28 12:28:37

Matlab实现高光谱图像分类:SVM、CNN、KNN与PaviaU实战解析

简介:面向高光谱遥感图像分类任务,这套Matlab实现选用Pavia University公开数据集,系统展示SVM、CNN与KNN在同一实验中的建模与比较过程,适用于遥感地物识别方向的研究生、工程师及算法学习者。包体共35个文件,包含27个…

作者头像 李华
网站建设 2026/10/28 12:25:04

微信PC端多开技术实现与优化方案

1. PC端微信多开需求解析微信作为国民级社交软件,在日常办公场景中经常需要同时登录多个账号。但官方客户端默认限制只能运行一个实例,这对需要同时处理工作号和个人号的用户造成了诸多不便。我通过逆向分析微信客户端的启动机制,发现其通过互…

作者头像 李华
网站建设 2026/10/28 12:25:02

油藏工程AI教学重构:从静态教材到动态知识图谱

1. 这不是PPT搬家,是油藏工程教学逻辑的AI重构“我的新版油藏工程教材搬到AI上了”——这句话刚在教研群里发出去,就有老教授回了一句:“教材还能搬?搬哪儿去?云盘?”我笑着回了张截图:学生用手…

作者头像 李华
网站建设 2026/10/28 12:24:44

Ubuntu下挂载NTFS磁盘的完整指南

1. 为什么需要在Ubuntu下挂载NTFS磁盘?作为一名长期使用Linux系统的开发者,我经常需要在Ubuntu和Windows双系统之间切换工作。NTFS(New Technology File System)是Windows的默认文件系统,而Ubuntu默认使用ext4文件系统…

作者头像 李华
网站建设 2026/10/28 12:24:41

ECC不只是椭圆曲线密码学:还谈内存纠错与SAP数据迁移

1. 椭圆曲线密码学:ECC凭什么成为现代加密的主力1.1 核心原理:同一条曲线上的加法游戏第一次接触椭圆曲线密码学的时候,我最大的困惑是:这玩意儿和椭圆到底有什么关系?数学课上学的椭圆是平面上的闭合曲线,…

作者头像 李华