160CM-035~037
终于放假了,有时间再来做几道题了。
1. 035、036
这两道题除了界面不一样,里面的执行流程基本都一样,关键字搜索一下就全部找到了。
固定密码,没什么好分析的,一看就明白。
2. 037
这道题也比较简单,搜索一下关键字就能找到关键程序段,虽然错误提示做了几项不同的随机提示内容,但通过正确提示字符串的位置,还是很容易确定关键跳转位置。由于比较用到了浮点计算,稍微有些不是很好理解,但只看结果的话,也是一样的。其中,正确的密码保存在0x401008
这个地址内,通过0040E111
进行浮点比较来判断输入内容是否与正确密码相同。
0040E0E8 > \8B4D E4 mov ecx,dword ptr ss:[ebp-0x1C] ; 取输入到密码
0040E0EB . 51 push ecx
0040E0EC . FF15 5C114100 call dword ptr ds:[<&MSVBVM50.__vbaR8Str>; msvbvm50.__vbaR8Str
0040E0F2 . DB43 4C fild dword ptr ds:[ebx+0x4C] ; 将整数转化为长双精压到st0
0040E0F5 . DD9D 38FFFFFF fstp qword ptr ss:[ebp-0xC8] ; 将st0弹出
0040E0FB . DCA5 38FFFFFF fsub qword ptr ss:[ebp-0xC8] ; 浮点减法运算
0040E101 . DFE0 fstsw ax ; 取协处理器的状态字
0040E103 . A8 0D test al,0xD
0040E105 . 0F85 EB030000 jnz CyberBla.0040E4F6
0040E10B . FF15 14114100 call dword ptr ds:[<&MSVBVM50.__vbaFpR8>>; msvbvm50.__vbaFpR8
0040E111 . DC1D 08104000 fcomp qword ptr ds:[0x401008] ; 比较 ST(0) 与 指定地址
0040E117 . DFE0 fstsw ax ; 指令取协处理器的状态字
0040E119 . F6C4 40 test ah,0x40
0040E11C . 74 05 je short CyberBla.0040E123
0040E11E . BF 01000000 mov edi,0x1
0040E123 > 8D4D E4 lea ecx,dword ptr ss:[ebp-0x1C]
0040E126 . FF15 8C114100 call dword ptr ds:[<&MSVBVM50.__vbaFreeS>; msvbvm50.__vbaFreeStr
0040E12C . 8D4D E0 lea ecx,dword ptr ss:[ebp-0x20]
0040E12F . FF15 90114100 call dword ptr ds:[<&MSVBVM50.__vbaFreeO>; msvbvm50.__vbaFreeObj
0040E135 . F7DF neg edi
0040E137 . 66:85FF test di,di
0040E13A 0F84 2C010000 je CyberBla.0040E26C ; 关键判断
其中,比较难理解的是浮点运算的状态寄存器的内容,如下图,fcomp指令比较,小于、等于、大于三种结果,都应的寄存器状态字分别为0100
、4000
和0000
,低8位都是0,没有问题,但是高8位的内容,和右侧按位显示的内容对应不上(4000
和3900
对应不上,其他两个可以认为能对应上)。
抛开状态寄存器内容的分析,找到正确的密码还是比较简单的。
3. 总结
为了理解状态寄存器中的各位的含义,查找了很久,总算找到了FPU状态寄存器各位的具体定义,但是参照这部分定义,也还是无法理解高8位的内容。功力不够,暂且不琢磨浮点运算的内容,以后需要用到的时候再去研究吧。
FPU状态寄存器(The status register ):
status状态寄存器是一个16位的寄存器,它里面每个二进制位的含义如下表所示:
Status Bit 状态位 |
Description 描述 |
0 |
Invalid operation exception flag 无效操作异常标志 |
1 |
Denormalized operand exception flag 非常规操作数异常标志 |
2 |
Zero divide exception flag 除零异常标志 |
3 |
Overflow exception flag 溢出异常(值太大时溢出)标志 |
4 |
Underflow exception flag 溢出异常(值太小时溢出)标志 |
5 |
Precision exception flag 精度异常标志 |
6 |
Stack fault 栈错误 |
7 |
Error summary status 错误摘要状态 |
8 |
Condition code bit 0 (C0) 条件代码位0 |
9 |
Condition code bit 1 (C1) 条件代码位1 |
10 |
Condition code bit 2 (C2) 条件代码位2 |
11-13 |
Top of stack pointer 栈顶指针 |
14 |
Condition code bit 3 (C3) 条件代码位3 |
15 |
FPU busy flag FPU正在计算中的忙标志 |
8、9、10及14这四个条件代码位用于配合浮点异常标志来提供一些额外的错误信息。
Error summary status(错误摘要状态位)用于当某异常发生时,如果control控制寄存器中对应的异常掩码位没被设置即没有屏蔽对应的异常,则该异常就会交由FPU执行默认处理,FPU默认处理时就会设置Error summary status标志位,并且丢弃掉错误的结果,同时FPU会产生一些异常信号来终止程序的继续执行,如果control里对应的异常掩码被设置时,对应的异常发生时就会被屏蔽掉,不会交由FPU执行默认处理,指令执行的异常结果将存储到对应的数据寄存器里,不会影响程序的继续执行,例如前面的fputest例子,默认情况下控制寄存器的掩码都是设置状态(屏蔽状态),异常的结果QNaN就会被存储在数据寄存器里而不会被FPU丢弃掉。
status状态寄存器的前6个标志位都用于指示FPU发生的异常,当FPU计算过程中发生浮点异常时,对应的异常标志位就会被设置,这些异常标志一旦被设置,就会一直保持设置状态,除非你手动清理掉它们(比如通过重新初始化FPU的方式),另外Error summary status位被设置时也会一直保留下去除非你手动清理掉它。
当FLD之类的指令导致寄存器栈溢出时,Stack fault栈错误标志就会被设置。
11到13位的栈顶指针用于表示当前的ST(0)栈顶寄存器对应哪个R寄存器,这个在前面fputest例子里,在gdb调试时info float命令输出的TOP值就存储在这三个标志位里。
在汇编程序中可以通过FSTSW指令来将status状态寄存器里的值读取到内存或读取到AX寄存器。
FPU控制寄存器(The control register):
控制寄存器用于控制FPU的精度,舍入方式等,控制寄存器也是一个16位的寄存器,该寄存器的各二进制位的含义如下表所示:
Control Bits 控制位 |
Description 描述 |
0 |
Invalid operation exception mask 无效操作异常掩码 |
1 |
Denormal operand exception mask 非常规操作数异常掩码 |
2 |
Zero divide exception mask 除零异常掩码 |
3 |
Overflow exception mask 值过大溢出异常掩码 |
4 |
Underflow exception mask 值过小溢出异常掩码 |
5 |
Precision exception mask 精度异常掩码 |
6–7 |
Reserved 保留位 |
8–9 |
Precision control 精度控制 |
10–11 |
Rounding control 舍入控制 |
12 |
Infinity control 仅用于兼容286 |
13–15 |
Reserved 保留位 |
头6位为异常掩码,当某个mask异常掩码被设置时,那么对应的异常发生时,就会屏蔽掉该异常,这里屏蔽的意思只是相对于FPU默认异常处理程序而言的,也就是不将异常交由FPU执行默认的处理操作,浮点的异常结果就不会被忽略掉。如果某mask异常掩码没被设置,就不会屏蔽该异常,异常发生时就会交由FPU执行默认的处理例程,默认处理例程中就会将发生异常的指令和产生的异常结果给丢弃掉,同时设置Error summary status错误摘要标志以表示默认例程捕获并处理了一个异常,还会产生异常信号来终止程序的继续执行。
初始状态下,所有的异常掩码默认都是设置即屏蔽状态,这样即便发生了对应的浮点异常也不会产生异常信号来终止程序的继续执行。
8到9位的Precision control精度控制位用于设置FPU内部数学计算时的浮点精度,可用的精度值如下:
-
00 — single-precision (24-bit significand)
单精度(24位有效二进制位)
-
01 — not used
没有使用
-
10 — double-precision (53-bit significand)
双精度(53位有效二进制位)
-
11 — double-extended-precision (64-bit significand)
双精度扩展(64位有效二进制位)
默认情况下,FPU是设置为双精度扩展,当你的某些计算中不需要很高的精度时,可以将FPU设置为单精度来加快浮点计算。
10到11位的Rounding control舍入控制位用于设置FPU如何对浮点计算的结果进行舍入操作,可用的舍入控制如下:
-
00 — round to nearest
舍入到最接近的值
-
01 — round down (toward negative infinity)
向下舍入(向负无穷大方向进行舍入)
-
10 — round up (toward positive infinity)
向上舍入(向正无穷大方向进行舍入)
-
11 — round toward zero
向零舍入
默认情况下,FPU是设置为round to nearest(舍入到最接近的值)。
初始状态下,control控制寄存器的默认值为0x037F,你可以使用FSTCW指令将控制寄存器的值加载到内存里。你也可以使用FLDCW指令来改变控制寄存器的值,FLDCW指令会将某内存位置里16位的值加载到控制寄存器,