用debug的反汇编和内存查看功能
-u 121F:01F3
从反汇编的结果可以看到,这个确实是一段调用DOS 21H显示字符串的代码,要显示的字符串的段偏移地址是 0263 ,在显示完这字符串之后,就调用 INT 21的4C号(MOV AH,4C 和 INT21H)功能退出了程序。
没有看到操作DS寄存器的内容,这有可能是数据可以代码都在同一个段里(121F)
上图中用 debug d指令查看了121F:0263地址内容,看到了这块内存保存字符串“Invalied password!”。
到这里,就开始有点接近目标了,这里就是校验密码失败后退出程序的地方,再往上一点代码,应该就是密码校验的地方,在被那就有可能找到密码存放的地方。
这时就需要一点一点对121F:01F3(B409 MOV AH,09)前面的地址进行反汇编,由于intel x86 CPU的指令不是定长的,因此选定的反汇编起始地址不对可能会得到不正确的指令。
用debug反汇编 121F:01CE处的代码
用 u 命令一直往前反汇编,在 121F:01CE 这个地方可以看到调用了DOS 21H的07号功能,这段代码是读取键盘输入的内容到寄存器AL,然后将AL的内容和 0xF0 这16进制数进行XOR运算结果保存到AL,最后把寄存器AL的内容保存到寄存器 SI 指向的内存地址。
这样看来,密码可能与 F0 做过XOR运算,因此只需要找到内存中的密码,用XOR就能还原出密码来。
继续往下反汇编
用debug反汇编121F:01E3处的代码
这里可以看到,这是一段字符串比较的代码,具体的细节可以查 intel 汇编语言,这里大概的意思就是把内存 0107 (没有指明段地址,默认还是121F)装载到寄存器CL,REPZ CMPSB 会根据 CX(CH被XOR指令设为0) 的值作为循环的次数比较SI 和 DI 指向的内存,每比较一个字符串,就SI、DI寄存器就加1,CX寄存器就减1。
MOV SI,0108
MOV DI,028D
毫无疑问,这里此时SI和DI保存的就是键盘输入的口令和程序预设密码的内存地址,至于预设密码的地址保存在SI还是DI,这可以往前反汇编查看键盘输入的口令保存在位置,这样可以反推出密码存放的地址。
在这程序里,从代码中可以看到,SI初始化为0108 (没有指明段地址,默认还是121F),而0107保存了要比较的字符串的长度,因此有理由判断密码可能保存在 121F:0108 这个地方。
于是执行debug 的d指令查看 121F:0107
用debug查看内存
这里可以看到0107处的内存是04,后面跟着4个C2,这里就可以比较肯定的判断,密码长度是4,密码的内容是4个C2,当然这密码是和前面提到 0xF0 进行XOR运算的结果。
现在,就开始尝试还原密码用计算器或者上网随便找个进制转换的网页进行进制转换。
换算的结果是
F0的二进制表示为11110000
C2对应的二进制是11000010
把 11000010 和 11110000做 XOR 运算得到
11110000
11000010 xor
00110010
得到的结果是 00110010,转换为16进制是0x32,通过查ASC码表得到对应的字符是'2',就是说密码是"2222"。
退出debug,再次执行README.EXE,输入密码 2222,最后密码校验通过,显示出里面的内容。