
下载,查壳,16位,IDA打开,是个汇编程序
代码分析:


这是一个DOS 平台的密码校验程序,采用单字节异或加密算法,读取用户输入后解密、与内置密文比对,最终输出对应结果并退出。以下按地址顺序逐条解析每条指令的功能、寄存器变化与程序作用。
程序头部与伪指令
seg001:0000 ; =========== SUBROUTINE ======================
seg001:0000 ; Attributes: noreturn
- IDA 反汇编生成的注释,说明以下是一个子程序,属性为
noreturn:执行到末尾不会返回调用点,而是直接退出程序。
seg001:0000 public start
- 汇编伪指令,声明
start标号为全局可见,作为程序的入口点,操作系统加载程序后会跳转到此处开始执行。
seg001:0000 start proc near
- 定义名为
start的过程,near表示这是段内近过程,仅能在当前代码段内被调用。
一、初始化数据段(0000 ~ 0005)
seg001:0000 mov ax, seg dseg
- 指令功能:将数据段
dseg的段地址送入通用寄存器ax。 - 背景:16 位汇编不允许立即数直接写入段寄存器,必须通过通用寄存器中转。
seg001:0003 mov ds, ax
- 指令功能:将
ax中的段地址写入数据段寄存器ds。 - 作用:此后所有默认内存访问(如
lodsb、[si]等)都会以ds为段基址,访问数据段中的缓冲区和常量。
seg001:0005 assume ds:dseg
- 汇编伪指令(不生成机器码),告知汇编器 “此时
ds指向dseg数据段”,用于汇编时的段合法性检查。
二、读取用户键盘输入(0005 ~ 000D)
seg001:0005 lea dx, asc_10000 ; "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"...
lea= 加载有效地址,将数据段中asc_10000的偏移地址送入dx。asc_10000是 IDA 为输入缓冲区起的名字,初始填充了$(0x24);ds:dx共同指向输入缓冲区的首地址。
seg001:0009 mov ah, 0Ah
- 为 DOS 系统调用设置功能号:
ah = 0Ah,对应缓冲式键盘输入功能。
seg001:000B int 21h ; DOS - BUFFERED KEYBOARD INPUT
; DS:DX -> buffer
- 触发
int 21hDOS 中断,执行 0Ah 号键盘输入功能:- 阻塞等待用户键盘输入,按下回车结束;
- 将输入字符存入
ds:dx指向的缓冲区; - 缓冲区结构:第 1 字节 = 最大容量,第 2 字节 = 实际输入字符数,第 3 字节起存储输入内容。
seg001:000D mov si, dx
- 将缓冲区地址
dx赋值给源变址寄存器si,为后续串操作读取字符做准备。
三、输入字符异或解密循环(000F ~ 001B)
seg001:000F lea si, asc_10000 ; "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"...
- 重新让
si指向输入缓冲区起始位置,确保源指针定位准确。
seg001:0013 mov cl, 10h
- 将立即数
0x10送入cl寄存器。 - 作用:
0x10是本程序的异或解密密钥。
seg001:0015 loc_10085: ; CODE XREF: start+1B↓j
- 循环入口标号;
CODE XREF说明下方001B处的jnz会跳转回此处。
seg001:0015 lodsb
- 串加载指令(Load String Byte):
- 操作:把
ds:si指向的 1 字节读入al,默认方向标志DF=0,执行后si += 1(自动指向下一个字节)。 - 本例:从输入缓冲区读取一个字符到
al。
- 操作:把
seg001:0016 xor al, cl
- 逻辑异或运算:
al = al ^ cl,即al = 输入字符ASCII码 ^ 0x10。 - 作用:对输入字符进行解密运算,是本程序的核心算法。
seg001:0018 stosb
- 串存储指令(Store String Byte):
- 操作:把
al的值写入es:di指向的内存单元,默认DF=0,执行后di += 1。 - 本例:将解密后的字符写回缓冲区,
di自动后移。 - 补充:该程序为 DOS 小模式 / COM 格式,加载时
es与ds指向同一段,因此写入位置与输入缓冲区在同一段内。
- 操作:把
seg001:0019 cmp al, 24h ; '$'
- 比较指令:计算
al - 0x24,仅修改标志位,不改变al的值。 0x24是字符$的 ASCII 码,也是 DOS 字符串的结束标志。- 作用:判断当前解密后的字符是否为结束符。
seg001:001B jnz short loc_10085
- 条件跳转:
jnz(结果不为 0 则跳转)。 - 逻辑:如果
al ≠ 0x24(还没到结束符),就跳回loc_10085,继续解密下一个字符,构成循环。
四、密码比对准备(001D ~ 0021)
seg001:001D lea si, asc_10000 ; "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"...
- 让
si重新指向输入缓冲区起点,此时缓冲区内容已完成解密。
seg001:0021 lea di, asc_10000+59h ; "$$$$$$$$$"
- 让
di指向asc_10000 + 0x59的内存位置,这里存放着程序内置的加密密文(正确密码加密后的数据)。
五、逐字节密码比对循环(0025 ~ 002F)
seg001:0025 loc_10095: ; CODE XREF: start+2F↓j
- 比对循环入口标号,下方
002F处的jmp会跳转回此处。
seg001:0025 lodsb
- 从用户解密后的密码中读取 1 字节到
al,si自动后移。
seg001:0026 cmp al, [di]
- 比较:用户解密后的字符(
al) vs 内置密文字符(ds:di指向的字节),根据结果设置标志位。
seg001:0028 jnz short loc_100A1
- 条件跳转:如果两个字符不相等,说明密码错误,直接跳转到错误提示分支
loc_100A1。
seg001:002A cmp al, 24h ; '$'
- 比较当前字符是否为结束符
$。如果比对到结束符还没跳转,说明前面所有字符都匹配成功。
seg001:002C jz short loc_100AB
- 条件跳转:
jz(结果为 0 则跳转)。 - 逻辑:如果
al == 0x24(全部匹配且到达结束符),跳转到成功提示分支loc_100AB。
seg001:002E inc di
di += 1,让指针指向下一个内置密文字符。
seg001:002F jmp short loc_10095
- 无条件跳转,回到比对循环入口,继续比对下一个字符。
六、密码错误分支(0031 ~ 0039)
seg001:0031 loc_100A1: ; CODE XREF: start+28↑j
- 错误分支入口,密码比对失败时跳转到这里。
seg001:0031 mov ah, 9
- 设置 DOS 功能号
09h,对应打印字符串功能。
seg001:0033 lea dx, asc_10000+47h ; "$$$$$$$$$$$$$$$$$$$$$$$"
- 让
dx指向错误提示字符串的首地址。
seg001:0037 int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
- 触发 DOS 中断,在屏幕输出
ds:dx指向的字符串,遇到$停止。 - 作用:输出密码错误的提示信息。
seg001:0039 jmp short loc_100B3
- 无条件跳转,直接跳到程序退出部分,跳过成功提示。
七、密码正确分支(003B ~ 0041)
seg001:003B loc_100AB: ; CODE XREF: start+2C↑j
- 成功分支入口,密码全部匹配时跳转到这里。
seg001:003B mov ah, 9
- 设置 DOS 功能号
09h,打印字符串。
seg001:003D lea dx, asc_10000+52h ; "$$$$$$$$$$$$$$$$"
- 让
dx指向成功提示字符串的首地址。
seg001:0041 int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
- 触发 DOS 中断,在屏幕输出成功提示信息。
八、程序退出(0043 ~ 0045)
seg001:0043 loc_100B3: ; CODE XREF: start+39↑j
- 程序统一出口,成功与失败最终都会汇聚到这里。
seg001:0043 mov ah, 4Ch
- 设置 DOS 功能号
4Ch,对应带退出码结束程序功能。
seg001:0045 int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
; AL = exit code
- 触发 DOS 中断,终止当前程序并返回操作系统;
al中的值作为程序退出码。
seg001:0045 start endp
- 伪指令,标记
start过程定义结束。
九、数据常量区(底部 db)
seg001:0047 db 4Eh, 6Fh, 74h, 20h, 65h, 71h, 75h, 61h, 6Ch, 21h, 24h
seg001:0052 db 45h, 71h, 75h, 61h, 6Ch, 21h, 24h, 58h, 54h, 53h, 44h
seg001:005D db 56h, 6Bh, 5Ah, 65h, 63h, 64h, 4Fh, 71h, 4Fh, 75h, 23h
seg001:0068 db 63h, 69h, 4Fh, 71h, 43h, 7Dh, 6Dh, 24h
db= 定义字节,在内存中写入一串原始字节数据。- 这些数据按偏移依次是:错误提示字符串、成功提示字符串、加密后的正确密码密文。
- 加密规则:
明文字符ASCII ^ 0x10 = 密文字节,逆向解密即可得到正确密码。
seg001:0068 seg001 ends
- 伪指令,标记
seg001段定义结束。
end start
- 伪指令,标记整个程序源码结束,并指定程序入口为
start。
核心逻辑总结
- 接收用户键盘输入;
- 每个输入字符与
0x10异或解密; - 解密结果与内置密文逐字节比对;
- 完全匹配输出成功,否则输出失败;
- 程序退出返回 DOS。
写解密脚本
cipher = [78, 111, 116, 32, 101, 113, 117, 97, 108, 33,
36, 69, 113, 117, 97, 108, 33, 36, 88, 84,
83, 68, 86, 107, 90, 101, 99, 100, 79, 113,
79, 117, 35, 99, 105, 79, 113, 67, 125, 109, 36]
flag=""
for i in cipher:
flag+=chr(i^0x10)
print(flag)
得到flag:

770

被折叠的 条评论
为什么被折叠?



