c++

寄存器jge和jae的问题

c++

Posted by YiMiTuMi on September 22, 2022

问题

问下面两个程序的执行结果是什么?

 unsigned short i; unsigned short index = 0; for(i = 0; i <index-1; i++){ printf("a\n"); }

 unsigned short i; unsigned long index = 0; for(i = 0; i <index-1; i++){ printf("b\n"); }

我这里写说结论,原因后面分析。

结论:

第一个程序,一次都不循环。 第二个程序是一个死循环。原因是跳转语句 jge 和 jae之间的差异。

需要一点汇编的flag寄存器的知识。

分析程序一

先看程序一,我们把语句分开一下比较清晰:

unsigned short i; 
unsigned short index = 0; 
for(i = 0; i <index-1; i++) //这个地方会发生溢出
{ 
	printf("a\n");
}

我们可以看到上面程序会发生溢出,当 index - 1 的时候溢出后,index的值会变成 65536(unsigned short的最大值),所以这个程序会一直死循环下去,但是并没有,我们来看一下汇编代码:

	int a = 0;
00CB59FB  mov         dword ptr [ebp-60h],0 
	unsigned short i; 
	unsigned short index = 0; 
00CB5A02  xor         eax,eax 
00CB5A04  mov         word ptr [ebp-78h],ax 
	for(i = 0; i <index-1; i++)
00CB5A08  xor         eax,eax 
00CB5A0A  mov         word ptr [ebp-6Ch],ax 
00CB5A0E  jmp         CCeshi4Dlg::OnInitDialog+15Ch (0CB5A1Ch) 
00CB5A10  mov         ax,word ptr [ebp-6Ch] 
00CB5A14  add         ax,1 
00CB5A18  mov         word ptr [ebp-6Ch],ax 
00CB5A1C  movzx       eax,word ptr [ebp-6Ch] 
00CB5A20  movzx       ecx,word ptr [ebp-78h] 
00CB5A24  sub         ecx,1     //这里将 index -1 保存到 ecx中
00CB5A27  cmp         eax,ecx   //将index 于 i进行比较
00CB5A29  jge         CCeshi4Dlg::OnInitDialog+184h (0CB5A44h) //最关键的跳转 
	{ 
		printf("a\n");
00CB5A2B  mov         esi,esp 
00CB5A2D  push        offset string "a\n" (0CDB294h) 
00CB5A32  call        dword ptr [__imp__printf (0CE3D3Ch)] 
00CB5A38  add         esp,4 
00CB5A3B  cmp         esi,esp 
00CB5A3D  call        @ILT+4275(__RTC_CheckEsp) (0CB30B8h) 
	}
00CB5A42  jmp         CCeshi4Dlg::OnInitDialog+150h (0CB5A10h) //回去继续执行for循环

主要看 00CB5A29 处的 jge 语句,如果过这个 jge 成立进行跳转则for循环结束。

jge成立的条件:

转移条件:sf异或of == 0
转移说明:大于等于转移
其他说明:有符号数,两个标志位sf和of

当我们执行完 cmp 时, Flag寄存器值为:

EFL = 0x00000213 = 001000010011

因为 jge 是一个有符号的指令所以,cmp(00CB5A27)判断是是两个有符号数 所以 eax = 0,ecx的值之前因为是无符号数做的sub所以结果为0xFFFFffff(补码),因为计算机保存的都是补码,我们将0xFFFFffff当作有符号数来转换为0x1000…0001(-1),所以eax > ecx条件成立。

sf在第8位,of在第12位,所以 sf = 0,of = 0,条件成立(0 异或 0 == 0),所以 jge跳转程序结束。

SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,sf = 1;如果非负,sf = 0。

注意:当我们将数据当作无符号数来运算,SF的值则没有意义,但是指令还是会改变SF的值。

分析程序二

再看程序二,我们把语句分开一下比较清晰:

unsigned short i; 
unsigned long index = 0; 
for(i = 0; i <index-1; i++) //这个地方会发生溢出
{ 
	printf("b\n");
}

我们可以看到上面程序会发生溢出,当 index - 1 的时候溢出后,index的值会变成 unsigned long的最大值,所以这个程序会一直死循环下去,我们来看一下汇编代码:

unsigned short i; 
	unsigned long index = 0; 
00F55A02  mov         dword ptr [ebp-78h],0 
	for(i = 0; i <index-1; i++)
00F55A09  xor         eax,eax 
00F55A0B  mov         word ptr [ebp-6Ch],ax 
00F55A0F  jmp         CCeshi4Dlg::OnInitDialog+15Dh (0F55A1Dh) 
00F55A11  mov         ax,word ptr [ebp-6Ch] 
00F55A15  add         ax,1 
00F55A19  mov         word ptr [ebp-6Ch],ax 
00F55A1D  movzx       eax,word ptr [ebp-6Ch] 
00F55A21  mov         ecx,dword ptr [ebp-78h] 
00F55A24  sub         ecx,1 
00F55A27  cmp         eax,ecx 
00F55A29  jae         CCeshi4Dlg::OnInitDialog+184h (0F55A44h) //最关键的跳转 
	{ 
		printf("b\n");
00F55A2B  mov         esi,esp 
00F55A2D  push        offset string "a\n" (0F7B294h) 
00F55A32  call        dword ptr [__imp__printf (0F83D3Ch)] 
00F55A38  add         esp,4 
00F55A3B  cmp         esi,esp 
00F55A3D  call        @ILT+4275(__RTC_CheckEsp) (0F530B8h) 
	}
00F55A42  jmp         CCeshi4Dlg::OnInitDialog+151h (0F55A11h) 

主要看 00F55A29 处的 jae 语句,如果过这个 jae 成立进行跳转则for循环结束。

jae成立的条件:

转移条件:cf == 0
转移说明:不低于,或者高于等于,或者进位标志转移清零时转移
其他说明:单个标志,无符号数
在转移指令之前有test、cmp等比较指令

当我们执行完 cmp 时, Flag寄存器值为:

EFL = 0x00000213 = 1000010011

cf在第1位,所以 cf = 1 条件不成立(cf != 0),所以 jge不会进行跳转程序进入死循环。

CF: 进位/借位标志,将两个操作数当做无符号数,在加减法指令下,若有进位或借位则CF为1,否则为0。

当执行cmp(00F55A27)指令时会进行减法操作从而改变cf的值。