逆向04:EFLAGS标志寄存器与指令

第一个CRACKME.exe

CRACKME下载

首先运行程序,观察其功能,发现在它的HelpRegister中有个验证的功能,随意输入字符串会弹出失败的提示。

CM失败提示

使用x64dbg打开CRACKME.exe后,直接RUN(F9)进入程序。

进入程序

通过之前的观察,不难发现其使用了一个MessageBoxA作为失败的提示,所以使用bp MessageBoxA下一个断点。

下MessageBoxA的断点

然后在CM程序中让程序调用这个MessageBoxA,发现反汇编窗口中已经成功中断程序。

MessageBoxA的位置

一个函数调用的时候,栈顶存储的就是函数的返回地址。

此时观察堆栈窗口,ESP的位置就是函数即将返回的地址,右键这个地址,在反汇编窗口中跟踪这个地址。

函数的调用位置

可以观察到,函数的调用位置就是在0x4013C1

x64dbg能判断函数名称是其通过传入的参数、调用的地址,找到其调用的dll分析的,dll里有导出表,导出表中有函数名。

在函数的调用位置的注释块上下可以找到No luck ...的信息,再往上可以找到Great work ...

所以先找到程序在哪里调用的No luck ...的逻辑,就可以找到程序判断的条件。因此在0x410364下断点,重新运行程序,找到其调用的位置。

在MessageBoxA的Beep上下断点,找到调用位置

MessageBoxA调用位置

je比较的位置下断点,运行程序,在je处中断时修改ZeroFlag为的值为1

JE断点与ZeroFlag

再运行程序,得到成功的提示。

成功提示

但此时仅为一次性破解,需要对程序进行修改以达到永久破解的目的。

je指令修改为jmp,跳过判断过程,然后保存可执行文件。

修改指令

此程序在Win10下运行时会弹出两次窗口,可能是兼容性问题。

EFLAGS 标志寄存器

所有的JCC都由标志寄存器控制

EFLAGS

带颜色的位是默认的。

其中:CF、PF、AP、ZF、SF、OF需要重点掌握。

在x64dbg中,EFLAGS/EFL的值为所有标志寄存器的16进制显示。

EFLAGS 00000245,转换成二进制为0010 0100 0101(省略0);CF为1,PF为1,ZF为1。

EFLAGS in x64dbg

进位标志寄存器:CF/Carry Flag

进位标志CF(Carry Flag):如果运算结果的最高位产生了一个进位或借位,那么其值为1,否则为0。

非最高位不进位时,CF值为0:

1
2
MOV EAX, 0x5555FFFF
ADD EAX, 1

最高位进位时,CF值为1:

1
2
MOV AL, 0xFF
ADD AL, 1

奇偶标志寄存器:PF/Parity Flag

奇偶标志寄存器反映运算结果中1的个数,如果1的个数是偶数,其值为1,否则为0。

当值为0时,其值也为1。

1
2
3
4
5
MOV AL, 0xFF
ADD AL, 1
MOV AL, 3
ADD AL, 3
ADD AL, 2

辅助进位标志寄存器: AF/Auxiliary Carry Flag

在发生以下情况时,辅助进位标志AF的值被设置位1,否则为0:

  1. 在字操作时,发生低字节像高字节进位或借位时
  2. 在字节操作时,发生低4位向高4位进位或借位时
1
2
MOV EAX, 0x55EEFFFF
ADD EAX, 2

运行后,辅助进位标志为1(满足条件1)。

1
2
MOV AX, 5EFE
ADD AX, 2

运行后,辅助进位标志为1(满足条件2)。

零标志寄存器: ZF/Zero Flag

如果运算结果为0,其值为1,否则为0。

1
XOR EAX,EAX

此时ZF为1。

1
MOV EAX,0

MOV不属于运算操作,不影响标志寄存器。

一个数与其本身异或,结果为0

符号标志寄存器: SF/Sign Flag

用于反映运算结果的符号位,与运算结果的最高位相同。

1
2
MOV AL, 7F
ADD AL, 2

此时SF为1。

溢出标志寄存器: OF/Overflow Flag

用于反映有符号数加减运算所得结果是否溢出。

如果运算结果超过当前运算位数所能表示的范围,则为溢出,其值为1,否则为0。

进位标志与溢出标志的区别:

  • 进位标志表示无符号数运算结果是否超出范围
  • 溢出标志表示有符号数运算结果是否超出范围

溢出主要是给有符号运算使用的,在有符号的运算中,有一下的规律:

1
2
3
4
5
正 + 正 = 正
正 + 正 = 负 有溢出
负 + 负 = 负
负 + 负 = 正 有溢出
正 + 负      无溢出情况

无符号运算看CF,有符号运算看OF。

  • 无符号、有符号都不溢出
1
2
MOV AL, 8
ADD AL, 8
  • 无符号溢出、有符号不溢出
1
2
MOV AL, 0xFF
ADD AL, 2
  • 无符号不溢出、有符号溢出
1
2
MOV AL, 0x7F
ADD AL, 2
  • 无符号、有福海都溢出
1
2
MOV AL, 0xFE
ADD AL, 0x80

方向标志寄存器 DF/Direction Flag

此寄存器用于控制串处理指令每次操作后SI, DI的增减。

DF=0时,每次操作后SI, DI递增。

DF=1时,每次操作后SI, DI递减。

计算指令

ADC 带进位加法

ADC指令将源操作数、进位标志位的值同时目的操作数相加。

格式:

1
ADC R/M, R/M/IMM # 两边不可同时为内存,且宽度需一致

使用案例:

1
2
3
ADC AL, CL
ADC BYTE PTR DS:[12FFC4], 2
ADD BYTE PTR DS:[12FFC4], AL

SBB 带借位减法

SBB指令将源操作数、进位标志位的值同时目的操作数相减。

格式同ADC 带进位加法

XCHG 交换数据

XCHG指令将目标操作数与源操作数的值进行交换。

格式:

1
ADC R/M, R/M # 两边不可同时为内存,且宽度需一致

使用案例:

1
2
3
XCHG AL, CL
XCHG BYTE PTR DS:[12FFC4], AL
XCHG DWORD PTR DS:[12FFC4], EAX

MOVS 内存间移动数据(串传送指令)

1
2
3
4
5
6
7
8
MOVSB
MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI]

MOVSW
MOVS WORD PTR ES:[EDI], WORD PTR DS:[ESI]

MOVSD
MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]

EDI, ESI可用于化简MOVS指令,且每次执行后EDI,ESI的值会加上操作数据宽度对应的地址差(如WORD宽度则 + 2)。此处EDI,ESI的加减可由DF/Direction Flag控制,0为加,1为减。

STOS 将寄存器AL/AX/EAX的值存储到EDI指定的内存单元

1
2
3
4
5
6
7
8
STOSB
STOS BYTE PTR ES:[EDI]

STOSW
STOS WORD PTR ES:[EDI]

STOSD
STOS DWORD PTR ES:[EDI]

执行后,EDI的值会根据DF递增或递减,如DWORD会递增会递减4。

REP 将计数寄存器ECX中指定的次数重复执行字符串指令

1
2
3
4
5
6
7
# 循环次数为16次
MOV ECX, 10

# MOVSD不会修改同一处地址,因为ESI, EDI会在MOVSD执行后更新
REP MOVSD

REP STOSD

Built with Hugo
主题 StackJimmy 设计