[系统安全16]加法与减法、乘法与除法优化原理

0x1 加法与减法的优化原理

1.1 加法的识别与优化

加法优化有3种方案:

1)形式1:变量与变量

2)形式2:变量加常量

3)形式3:变量加1

C源代码:

int _tmain(int argc, _TCHAR* argv[])
{
	int nNum, nA = 8;
	nNum = argc + nA;      // 形式1:变量减变量
	printf("%d\r\n",nNum);
	nNum = argc + 9;       // 形式2:变量减常量
	printf("%d\r\n",nNum);
	nNum = nNum + 1;       // 形式3:变量减1
	printf("%d\r\n",nNum);

	return 0;
}

Debug反汇编:

01297710 55            push ebp
01297711 8BEC          mov ebp,esp
01297713 81EC D8000000 sub esp,0xD8
01297719 53            push ebx
0129771A 56            push esi                   ;  9-34.<ModuleEntryPoint>
0129771B 57            push edi
0129771C 8DBD 28FFFFFF lea edi,[local.54]
01297722 B9 36000000   mov ecx,0x36
01297727 B8 CCCCCCCC   mov eax,0xCCCCCCCC
0129772C F3:AB         rep stos dword ptr es:[edi]
0129772E C745 EC 08000 mov [local.5],0x8          ;  nA = 8
01297735 8B45 08       mov eax,[arg.1]            ;  eax = argc 赋值
01297738 0345 EC       add eax,[local.5]          ;  eax = eax + nA;形式1:nNum = agrc + nA
0129773B 8945 F8       mov [local.2],eax          ;  nNum = eax
0129773E 8B45 F8       mov eax,[local.2]          ;  eax = nNum
01297741 50            push eax
01297742 68 508E3201   push 9-34.01328E50         ;  %d\r\n
01297747 E8 C8C5FFFF   call 9-34.01293D14         ;  printf
0129774C 83C4 08       add esp,0x8                ;  堆栈平衡
0129774F 8B45 08       mov eax,[arg.1]            ;  eax = argc
01297752 83C0 09       add eax,0x9                ;  eax = eax + 9;argc + 9;形式2:nNum = argc + 9
01297755 8945 F8       mov [local.2],eax          ;  nNum = eax
01297758 8B45 F8       mov eax,[local.2]          ;  eax = nNum
0129775B 50            push eax
0129775C 68 508E3201   push 9-34.01328E50         ;  %d\r\n
01297761 E8 AEC5FFFF   call 9-34.01293D14         ;  printf
01297766 83C4 08       add esp,0x8
01297769 8B45 F8       mov eax,[local.2]          ;  eax = nNum
0129776C 83C0 01       add eax,0x1                ;  eax = eax + 1;形式3:nNum = nNum+1
0129776F 8945 F8       mov [local.2],eax          ;  nNum = eax
01297772 8B45 F8       mov eax,[local.2]          ;  eax = nNum
01297775 50            push eax
01297776 68 508E3201   push 9-34.01328E50         ;  %d\r\n
0129777B E8 94C5FFFF   call 9-34.01293D14         ;  printf
01297780 83C4 08       add esp,0x8
01297783 33C0          xor eax,eax
01297785 5F            pop edi                    ;  9-34.<ModuleEntryPoint>
01297786 5E            pop esi                    ;  9-34.<ModuleEntryPoint>
01297787 5B            pop ebx                    ;  9-34.<ModuleEntryPoint>
01297788 81C4 D8000000 add esp,0xD8
0129778E 3BEC          cmp ebp,esp
01297790 E8 94ABFFFF   call 9-34.01292329
01297795 8BE5          mov esp,ebp
01297797 5D            pop ebp                    ;  9-34.<ModuleEntryPoint>
01297798 C3            retn

Release反汇编:

00EB1260 push ebp
00EB1261 mov ebp,esp
00EB1263 push esi
00EB1264 mov esi,[arg.1]                                              ;  esi = argc
00EB1267 lea eax,dword ptr ds:[esi+0x8]                               ;  eax = argc+nA;形式1:变量+变量;优化后的加法,精妙!
00EB126A push eax
00EB126B push 9-34.00EC64D8                                           ;  %d\r\n
00EB1270 call 9-34.printf_initialize_sse2_sse2_from_osKeywordOncrt_st>;  printf
00EB1275 add esi,0x9                                                  ;  esi = esi+9;argc+9;形式2:变量+常量
00EB1278 push esi
00EB1279 push 9-34.00EC64D8                                           ;  %d\r\n
00EB127E call 9-34.printf_initialize_sse2_sse2_from_osKeywordOncrt_st>;  printf
00EB1283 lea eax,dword ptr ds:[esi+0x1]                               ;  eax = esi + 1;nNum+1;形式3:变量+1;直接用esi进行地址的存储
00EB1286 push eax
00EB1287 push 9-34.00EC64D8                                           ;  %d\r\n
00EB128C call 9-34.printf_initialize_sse2_sse2_from_osKeywordOncrt_st>
00EB1291 add esp,0x18
00EB1294 xor eax,eax
00EB1296 pop esi                                                      ;  9-34.__argc_log10_table_tk_exit_tableate
00EB1297 pop ebp                                                      ;  9-34.__argc_log10_table_tk_exit_tableate
00EB1298 retn
          

小结:

1)变量+变量 = lea Reg32,[变量+变量]

2)变量+常量 = add 变量+常量

3)变量+1 = lea Reg32,[变量+1]

注:这里的代码我和书里不一样,书中是【变量+1 = inc 常量】

1.2 减法的识别与优化

减法优化与加法大同小异。
C源代码:

int _tmain(int argc, _TCHAR* argv[])
{
	int nNum, nA = 8;
	nNum = argc - nA;      // 形式1:变量减变量
	printf("%d\r\n",nNum);
	nNum = argc - 9;       // 形式2:变量减常量
	printf("%d\r\n",nNum);
	nNum = nNum - 1;       // 形式3:变量减1
	printf("%d\r\n",nNum);

	return 0;
}

Debug反汇编:

00E07710 push ebp
00E07711 mov ebp,esp
00E07713 sub esp,0xD8
00E07719 push ebx
00E0771A push esi                   ;  9-34.<ModuleEntryPoint>
00E0771B push edi                   ;  9-34.<ModuleEntryPoint>
00E0771C lea edi,[local.54]
00E07722 mov ecx,0x36
00E07727 mov eax,0xCCCCCCCC
00E0772C rep stos dword ptr es:[edi]
00E0772E>mov [local.5],0x8          ;  nA = 8
00E07735 mov eax,[arg.1]            ;  eax = argc
00E07738 sub eax,[local.5]          ;  eax = eax - nA; eax = argc-nA;形式1:变量-变量
00E0773B mov [local.2],eax          ;  nNum = eax
00E0773E mov eax,[local.2]          ;  kernel32.BaseThreadInitThunk
00E07741 push eax
00E07742 push 9-34.00E98E50         ;  %d\r\n
00E07747 call 9-34.00E03D14         ;  printf
00E0774C add esp,0x8                ;  调用者堆栈平衡
00E0774F mov eax,[arg.1]            ;  eax = argc
00E07752 sub eax,0x9                ;  eax = eax - 9;argc-9;形式2:变量-常量
00E07755 mov [local.2],eax          ;  nNum = eax
00E07758 mov eax,[local.2]          ;  kernel32.BaseThreadInitThunk
00E0775B push eax
00E0775C push 9-34.00E98E50         ;  %d\r\n
00E07761 call 9-34.00E03D14         ;  printf
00E07766 add esp,0x8
00E07769 mov eax,[local.2]          ;  eax = nNum
00E0776C sub eax,0x1                ;  eax = eax - 1;nNum=nNum-1;形式3:变量-1
00E0776F mov [local.2],eax          ;  nNum = eax
00E07772 mov eax,[local.2]          ;  kernel32.BaseThreadInitThunk
00E07775 push eax
00E07776 push 9-34.00E98E50         ;  %d\r\n
00E0777B call 9-34.00E03D14         ;  printf
00E07780 add esp,0x8
00E07783 xor eax,eax
00E07785 pop edi                    ;  kernel32.74EE38F4
00E07786 pop esi                    ;  kernel32.74EE38F4
00E07787 pop ebx                    ;  kernel32.74EE38F4
00E07788 add esp,0xD8
00E0778E cmp ebp,esp
00E07790 call 9-34.00E02329
00E07795 mov esp,ebp
00E07797 pop ebp                    ;  kernel32.74EE38F4
00E07798 retn

Release反汇编:

012A1260 push ebp
012A1261 mov ebp,esp
012A1263 push esi                      ;9-34.<ModuleEntryPoint>
012A1264 mov esi,[arg.1]               ;esi = nA
012A1267 lea eax,dword ptr ds:[esi-0x8];esi-8;nA-8;形式1:变量-变量
012A126A push eax
012A126B push 9-34.012B64D8            ;%d\r\n
012A1270 call 9-34.printf>;  printf
012A1275 add esi,-0x9                  ;esi = esi-9;argc-9;形式2:变量-常量;-0x9是补码
012A1278 push esi                      ;9-34.<ModuleEntryPoint>
012A1279 push 9-34.012B64D8            ;%d\r\n
012A127E call 9-34.printf>
012A1283 lea eax,dword ptr ds:[esi-0x1];eax = esi-1;nNum-1;形式3:变量-1
012A1286 push eax
012A1287 push 9-34.012B64D8            ;%d\r\n
012A128C call 9-34.printf>
012A1291 add esp,0x18
012A1294 xor eax,eax
012A1296 pop esi                       ;kernel32.74EE38F4
012A1297 pop ebp                       ;kernel32.74EE38F4
012A1298 retn

小结:

1)变量-变量 = lea Reg32,[变量-变量]

2)变量-常量 = add 变量+补码(常量)

3)变量-1 = lea Reg32,[变量-1]

注:这里的代码我和书里不一样,书中是【dec 变量】

0x2 乘法与除法的优化原理

左右移位就相当于在做乘除计算,左移1位相当于乘以2,右移1位相当于除以2。

2.1 乘法

2.1.1 乘法的位移优化

当乘数为2的整数次方、且大于8时,编译器才会使用优化。
c源代码:

int _tmain(int argc, _TCHAR* argv[])
{
	int nNum = 16;
	printf("%p",nNum*argc);

	return 0;
}

Debug汇编

008D7710 push ebp
008D7711 mov ebp,esp
008D7713 sub esp,0xCC
008D7719 push ebx
008D771A push esi                   ;  9-35.<ModuleEntryPoint>
008D771B push edi                   ;  9-35.<ModuleEntryPoint>
008D771C lea edi,[local.51]
008D7722 mov ecx,0x33
008D7727 mov eax,0xCCCCCCCC
008D772C rep stos dword ptr es:[edi]
008D772E mov [local.2],0x10         ;  局部变量nNum = 10;0x10等于十进制16
008D7735 mov eax,[local.2]          ;  eax = 局部变量nNum
008D7738 imul eax,[arg.1]           ;  eax = eax*argc;乘法运算
008D773C push eax
008D773D push 9-35.00968E50         ;  %p
008D7742 call 9-35.008D3D14         ;  printf
008D7747 add esp,0x8
008D774A xor eax,eax
008D774C pop edi                    ;  kernel32.74EE38F4
008D774D pop esi                    ;  kernel32.74EE38F4
008D774E pop ebx                    ;  kernel32.74EE38F4
008D774F add esp,0xCC
008D7755 cmp ebp,esp
008D7757 call 9-35.008D2329
008D775C mov esp,ebp
008D775E pop ebp                    ;  kernel32.74EE38F4
008D775F retn

Release汇编:

000E1260 push ebp
000E1261 mov ebp,esp
000E1263 mov eax,[arg.1]
000E1266 shl eax,0x4  ;  左移动0x4位;左移动乘以4;shl逻辑左移指令[2^4=16]
000E1269 push eax
000E126A push 9-35.000F64D8  ;  %p
000E126F call 9-35.printf>
000E1274 add esp,0x8
000E1277 xor eax,eax
000E1279 pop ebp     ;  kernel32.74EE38F4
000E127A retn

测试:

1)如果结果比2的次方多1会采用加法

2)如果结果比2的次方少1会采用减法

2.1.2 乘法的lea指令优化

C源代码:

int _tmain(int argc, _TCHAR* argv[])
{
	int a = 1, b, c, d, e, f, g;
	b = argc+a*4+6;	// 形式1
	c = argc+a*3+6; // 形式2
	d = argc*2; 	// 形式3
	e = argc*3; 	// 形式4
	f = argc*4; 	// 形式5
	g = argc*11; 	// 形式6
	printf("%d %d %d %d %d %d", b, c, d, e, f, g);

	return 0;
}

Debug汇编:

00DC7710 push ebp
00DC7711 mov ebp,esp
00DC7713 sub esp,0x114
00DC7719 push ebx
00DC771A push esi                            ;  9-36.<ModuleEntryPoint>
00DC771B push edi
00DC771C lea edi,[local.69]
00DC7722 mov ecx,0x45
00DC7727 mov eax,0xCCCCCCCC
00DC772C rep stos dword ptr es:[edi]
00DC772E>mov [local.2],0x1                   ;  给局部变量a赋值
00DC7735 mov eax,[local.2]                   ;  eax = a
00DC7738 mov ecx,[arg.1]                     ;  ecx = argc
00DC773B lea edx,dword ptr ds:[ecx+eax*4+0x6];  edx = argc+a*4+6
00DC773F mov [local.5],edx                   ;  局部变量b = edx
00DC7742 imul eax,[local.2],0x3              ;  先做a*3
00DC7746 mov ecx,[arg.1]                     ;  ecx = argc
00DC7749 lea edx,dword ptr ds:[ecx+eax+0x6]  ;  edx = 【ecx+eax+0x6】;edx = argc + (a*3) + 0x6
00DC774D mov [local.8],edx                   ;  局部变量c = edx
00DC7750 mov eax,[arg.1]                     ;  eax = argc
00DC7753 shl eax,1                           ;  argc*2;左移1位,相当于乘以2
00DC7755 mov [local.11],eax                  ;  赋值给局部变量d
00DC7758 imul eax,[arg.1],0x3                ;  使用乘法指令;argc*3
00DC775C mov [local.14],eax                  ;  局部变量e = eax
00DC775F mov eax,[arg.1]                     ;  eax = argc
00DC7762 shl eax,0x2                         ;  eax = eax*4;左移2位等于乘以4
00DC7765 mov [local.17],eax                  ;  局部变量f = eax
00DC7768 imul eax,[arg.1],0xB                ;  eax = argc*11;B等于十六进制的12
00DC776C mov [local.20],eax                  ;  局部变量g = eax
00DC776F mov eax,[local.20]
00DC7772 push eax
00DC7773 mov ecx,[local.17]
00DC7776 push ecx
00DC7777 mov edx,[local.14]
00DC777A push edx
00DC777B mov eax,[local.11]
00DC777E push eax
00DC777F mov ecx,[local.8]
00DC7782 push ecx
00DC7783 mov edx,[local.5]
00DC7786 push edx
00DC7787 push 9-36.00E58E50                  ;  %d %d %d %d %d %d
00DC778C call 9-36.00DC3D14                  ;  printf
00DC7791 add esp,0x1C
00DC7794 xor eax,eax

Release版反汇编代码:


01271000 push ebp
01271001 mov ebp,esp
01271003 mov ecx,[arg.1]                 ;  exc = argc;argc=1
01271006 imul eax,ecx,0xB                ;  g = argc*11
01271009 push eax
0127100A lea eax,dword ptr ds:[ecx*4]    ;  f = argc*4
01271011 push eax
01271012 lea eax,dword ptr ds:[ecx+ecx*2];  e = argc*3;argc=1+1*2
01271015 push eax
01271016 lea eax,dword ptr ds:[ecx+ecx]  ;  d = argc*2;优化成了加法 1+1
01271019 push eax
0127101A lea eax,dword ptr ds:[ecx+0x9]  ;  c = argc+9; 把a*3+6计算完毕;
0127101D push eax
0127101E lea eax,dword ptr ds:[ecx+0xA]  ;  b = argc+a*4 把a*4+6计算完毕;
01271021 push eax
01271022 push 9-36.01286448              ;  ASCII 25,"d %d %d %d %d %d"
01271027 call 9-36.printf_>
0127102C add esp,0x1C
0127102F xor eax,eax
01271031 pop ebp
01271032 retn 

2.1.3 除法与倒数相乘

编译器世界中倒数相乘的中心思想其实就是用乘法来代替除法运算。它的原理很简单,就是将被除数乘以除数的倒数,其公式为x/y = x*(1/y),我们拿10/2作为例子,我可以得出以下推论:

由 公式x/y = x(1/y) 可得 10/2 = 10(1/2) = 10*0.5

这里没有写反汇编后的状态

2.1.4 倒数相乘与定点运算的配合

理论知识,没仔细去看

除法

2.1.1 除法的识别与优化

这一章虽然讲了编译器对除法的优化,但是不做笔记。

2.2.2 取模运算的识别与优化

不理解,略过,需要用时回头再看

小结

普通除法

mov  Reg32_1,XXXXXXXXh
imul Reg32_2 (被除数)
...
mov  Reg32_1,edx
shr  Reg32_1,1Fh
add  Reg32_1,edx

应用计算结果(如果倒数被向上圆整了,那么根据sar指令后的数值向下圆整即可)如下:

(除数=XXXXXXXXh*2^-32)

除数为2的次方

mov eax,(被除数)
cdq
add edx,XXh
add eax,edx
sar eax,YYh

应用计算结果如下:

(除数 = XXh + 1 或 2^YYh)

posted @ 2017-11-01 11:55  17bdw  阅读(891)  评论(0编辑  收藏  举报