1.if else模式:case语句块不超过3条
通过O2优化后的代码来看, if else if 结构会在条件跳转后紧跟语句块,而switch结构则将所有的条件跳转都放在一起。
特征1:
sub eax,case1
je add1
sub eax,case2
je add2
sub eax,case3
jne addend
特征2:
语句
mov esp,ebp
pop ebp
ret
语句
mov esp,ebp
pop ebp
ret
语句
mov esp,ebp
pop ebp
ret
2.线性结构模式:每两个case间的差值小于7
特征1:LastBreakAdd和TableAdd挨得较近 且 特征2的地址中会包括LastBreakAdd
dec eax
cmp eax,CaseMax-1
ja LastBreakAdd
jmp [eax*4+TableAdd]
特征2:代码段中出现,连续增大,偶有出现LastBreakAdd
地址
地址
地址
地址
……
3.地址表模式:两个相邻case间的差值大于6,CaseMax<256
特征1:
dec eax
cmp eax,CaseMax-1
ja LastBreakAdd
movzx eax, byte[eax+IndexTableAdd]
jmp [eax*4+TableAdd]
特征2:连续增大的地址块无逆序,索引块以IndexMax为主导,间有从0到IndexMax-1的依次增大的自然数
地址块
索引块
4.判定树模式:CaseMax>255 又称混合模式 子树优化优先
特征1:
cmp eax,Middle
jg RightTreeAdd
je MiddleAdd
add eax, balance
cmp eax,LeftMax-blance
jg LastBreakAdd
jmp AddTable
特征2:
同时见前三种模式特征中的两到三种。
if模式
void test_switch_if()
{
00408320 55 push ebp
00408321 8B EC mov ebp,esp
00408323 51 push ecx
int i = 1;
scanf("%d", &i);
00408324 8D 45 FC lea eax,[i]
00408327 C7 45 FC 01 00 00 00 mov dword ptr [i],1
0040832E 50 push eax
0040832F 68 50 CE 4A 00 push offset string "%d" (04ACE50h)
00408334 E8 67 B0 FF FF call _scanf (04033A0h)
switch(i){
00408339 8B 45 FC mov eax,dword ptr [i]
0040833C 83 C4 08 add esp,8
0040833F 83 E8 01 sub eax,1
00408342 74 2C je test_switch_if+50h (0408370h)
00408344 83 E8 02 sub eax,2
00408347 74 16 je test_switch_if+3Fh (040835Fh)
00408349 83 E8 61 sub eax,61h
0040834C 75 2F jne test_switch_if+5Dh (040837Dh)
case 100:
printf("i == 100");
0040834E 68 64 CE 4A 00 push offset string "i == 100" (04ACE64h)
00408353 E8 B6 BF FF FF call _printf (040430Eh)
00408358 83 C4 04 add esp,4
break;
0040835B 8B E5 mov esp,ebp
0040835D 5D pop ebp
0040835E C3 ret
case 3:
printf("i == 3");
0040835F 68 5C CE 4A 00 push offset string "i == 3" (04ACE5Ch)
00408364 E8 A5 BF FF FF call _printf (040430Eh)
00408369 83 C4 04 add esp,4
break;
0040836C 8B E5 mov esp,ebp
0040836E 5D pop ebp
0040836F C3 ret
case 1:
printf("i == 1");
00408370 68 54 CE 4A 00 push offset string "i == 1" (04ACE54h)
00408375 E8 94 BF FF FF call _printf (040430Eh)
0040837A 83 C4 04 add esp,4
break;
0040837D 8B E5 mov esp,ebp
0040837F 5D pop ebp
00408380 C3 ret
地址表模式
void SwitchLine()
{
00408240 55 push ebp
00408241 8B EC mov ebp,esp
00408243 51 push ecx
int nIndex = 0;
scanf("%d", &nIndex);
00408244 8D 45 FC lea eax,[nIndex]
00408247 C7 45 FC 00 00 00 00 mov dword ptr [nIndex],0
0040824E 50 push eax
0040824F 68 50 CE 4A 00 push offset string "%d" (04ACE50h)
00408254 E8 47 B1 FF FF call _scanf (04033A0h)
switch(nIndex)
00408259 8B 45 FC mov eax,dword ptr [nIndex]
0040825C 83 C4 08 add esp,8
0040825F 48 dec eax
00408260 83 F8 06 cmp eax,6
00408263 77 69 ja $LN9+0Dh (04082CEh)
00408265 FF 24 85 D4 82 40 00 jmp dword ptr [eax*4+4082D4h]
case 1:
printf("nIndex == 1");
0040826C 68 70 CE 4A 00 push offset string "nIndex == 1" (04ACE70h)
00408271 E8 98 C0 FF FF call _printf (040430Eh)
00408276 83 C4 04 add esp,4
break;
00408279 8B E5 mov esp,ebp
0040827B 5D pop ebp
0040827C C3 ret
break;
case 2:
printf("nIndex == 2");
0040827D 68 80 CE 4A 00 push offset string "nIndex == 2" (04ACE80h)
00408282 E8 87 C0 FF FF call _printf (040430Eh)
00408287 83 C4 04 add esp,4
break;
0040828A 8B E5 mov esp,ebp
0040828C 5D pop ebp
0040828D C3 ret
case 3:
printf("nIndex == 3");
0040828E 68 90 CE 4A 00 push offset string "nIndex == 3" (04ACE90h)
00408293 E8 76 C0 FF FF call _printf (040430Eh)
00408298 83 C4 04 add esp,4
break;
0040829B 8B E5 mov esp,ebp
0040829D 5D pop ebp
0040829E C3 ret
case 5:
printf("nIndex == 5");
0040829F 68 A0 CE 4A 00 push offset string "nIndex == 5" (04ACEA0h)
004082A4 E8 65 C0 FF FF call _printf (040430Eh)
004082A9 83 C4 04 add esp,4
break;
004082AC 8B E5 mov esp,ebp
004082AE 5D pop ebp
004082AF C3 ret
case 6:
printf("nIndex == 6");
004082B0 68 B0 CE 4A 00 push offset string "nIndex == 6" (04ACEB0h)
004082B5 E8 54 C0 FF FF call _printf (040430Eh)
004082BA 83 C4 04 add esp,4
break;
004082BD 8B E5 mov esp,ebp
004082BF 5D pop ebp
004082C0 C3 ret
case 7:
printf("nIndex == 7");
004082C1 68 C0 CE 4A 00 push offset string "nIndex == 7" (04ACEC0h)
004082C6 E8 43 C0 FF FF call _printf (040430Eh)
004082CB 83 C4 04 add esp,4
break;
004082CE 8B E5 mov esp,ebp
004082D0 5D pop ebp
004082D1 C3 ret
;nop
004082D2 66 90 xchg ax,ax
;下面是case地址块 不是代码 VS2015的反汇编搞差了 第一个是 6C 82 40 00 后面跟了另外6个地址
004082D4 6C ins byte ptr es:[edi],dx
004082D5 82 40 00 7D add byte ptr [eax],7Dh
004082D9 82 40 00 8E add byte ptr [eax],8Eh
004082DD 82 40 00 CE add byte ptr [eax],0CEh
004082E1 82 40 00 9F add byte ptr [eax],9Fh
004082E5 82 40 00 B0 add byte ptr [eax],0B0h
004082E9 82 40 00 C1 add byte ptr [eax],0C1h
004082ED 82 40 00 CC add byte ptr [eax],0CCh
索引表模式
void test_switch_nonline()
{
004083A0 55 push ebp
004083A1 8B EC mov ebp,esp
004083A3 51 push ecx
int i = 0;
scanf("%d", &i);
004083A4 8D 45 FC lea eax,[i]
004083A7 C7 45 FC 00 00 00 00 mov dword ptr [i],0
004083AE 50 push eax
004083AF 68 50 CE 4A 00 push offset string "%d" (04ACE50h)
004083B4 E8 E7 AF FF FF call _scanf (04033A0h)
switch(i){
004083B9 8B 45 FC mov eax,dword ptr [i]
004083BC 83 C4 08 add esp,8
004083BF 48 dec eax
004083C0 83 F8 0E cmp eax,0Eh
004083C3 77 70 ja $LN9+0Dh (0408435h)
004083C5 0F B6 80 58 84 40 00 movzx eax,byte ptr [eax+408458h]
;movzx无符号扩展并传送,用于将字节型索引转换为32位
004083CC FF 24 85 3C 84 40 00 jmp dword ptr [eax*4+40843Ch]
case 1:
printf("i == 1");
004083D3 68 54 CE 4A 00 push offset string "i == 1" (04ACE54h)
004083D8 E8 31 BF FF FF call _printf (040430Eh)
004083DD 83 C4 04 add esp,4
break;
004083E0 8B E5 mov esp,ebp
004083E2 5D pop ebp
004083E3 C3 ret
case 2:
printf("i == 2");
004083E4 68 D0 CE 4A 00 push offset string "i == 2" (04ACED0h)
004083E9 E8 20 BF FF FF call _printf (040430Eh)
004083EE 83 C4 04 add esp,4
break;
004083F1 8B E5 mov esp,ebp
004083F3 5D pop ebp
004083F4 C3 ret
case 3:
printf("i == 3");
004083F5 68 5C CE 4A 00 push offset string "i == 3" (04ACE5Ch)
004083FA E8 0F BF FF FF call _printf (040430Eh)
004083FF 83 C4 04 add esp,4
break;
00408402 8B E5 mov esp,ebp
00408404 5D pop ebp
00408405 C3 ret
case 5:
printf("i == 5");
00408406 68 D8 CE 4A 00 push offset string "i == 5" (04ACED8h)
0040840B E8 FE BE FF FF call _printf (040430Eh)
00408410 83 C4 04 add esp,4
break;
00408413 8B E5 mov esp,ebp
00408415 5D pop ebp
00408416 C3 ret
case 6:
printf("i == 6");
00408417 68 E0 CE 4A 00 push offset string "i == 6" (04ACEE0h)
0040841C E8 ED BE FF FF call _printf (040430Eh)
00408421 83 C4 04 add esp,4
break;
00408424 8B E5 mov esp,ebp
00408426 5D pop ebp
00408427 C3 ret
case 15:
printf("i == 15");
00408428 68 E8 CE 4A 00 push offset string "i == 15" (04ACEE8h)
0040842D E8 DC BE FF FF call _printf (040430Eh)
00408432 83 C4 04 add esp,4
break;
00408435 8B E5 mov esp,ebp
00408437 5D pop ebp
00408438 C3 ret
;nop
00408439 0F 1F 00 nop dword ptr [eax]
;下面是case地址块 第一个是00 40 83 D3
0040843C D3 83 40 00 E4 83 rol dword ptr [ebx-7C1BFFC0h],cl
00408442 40 inc eax
00408443 00 F5 add ch,dh
00408445 83 40 00 06 add dword ptr [eax],6
00408449 84 40 00 test byte ptr [eax],al
0040844C 17 pop ss
0040844D 84 40 00 test byte ptr [eax],al
00408450 28 84 40 00 35 84 40 sub byte ptr [eax+eax*2+40843500h],al
; 00408458开始为case语句块索引表
00408457 00 00 add byte ptr [eax],al
00408459 01 02 add dword ptr [edx],eax
0040845B 06 push es
0040845C 03 04 06 add eax,dword ptr [esi+eax]
0040845F 06 push es
00408460 06 push es
00408461 06 push es
00408462 06 push es
00408463 06 push es
00408464 06 push es
00408465 06 push es
00408466 05 CC CC CC CC add eax,0CCCCCCCCh
混合模式:本例 左子树优化为地址表模式,右子树优化为if模式
void test_switch_tree()
{
004084A0 55 push ebp
004084A1 8B EC mov ebp,esp
004084A3 51 push ecx
int i = 8;
scanf("%d", &i);
004084A4 8D 45 FC lea eax,[i]
004084A7 C7 45 FC 08 00 00 00 mov dword ptr [i],8
004084AE 50 push eax
004084AF 68 50 CE 4A 00 push offset string "%d" (04ACE50h)
004084B4 E8 E7 AE FF FF call _scanf (04033A0h)
switch(i){
004084B9 8B 45 FC mov eax,dword ptr [i]
004084BC 83 C4 08 add esp,8
004084BF 83 F8 23 cmp eax,23h
004084C2 7F 66 jg $LN7+22h (040852Ah)
004084C4 74 53 je $LN7+11h (0408519h)
004084C6 83 C0 FE add eax,0FFFFFFFEh
;这里是下标平衡值 0FFFFFFFEh表示-2
004084C9 83 F8 08 cmp eax,8
004084CC 77 6F ja $LN7+35h (040853Dh)
004084CE FF 24 85 84 85 40 00 jmp dword ptr [eax*4+408584h]
case 2:
printf("i == 2\n");
004084D5 68 F4 CE 4A 00 push offset string "i == 2\n" (04ACEF4h)
004084DA E8 2F BE FF FF call _printf (040430Eh)
004084DF 83 C4 04 add esp,4
break;
004084E2 8B E5 mov esp,ebp
004084E4 5D pop ebp
004084E5 C3 ret
case 3:
printf("i == 3\n");
004084E6 68 00 CF 4A 00 push offset string "i == 3\n" (04ACF00h)
004084EB E8 1E BE FF FF call _printf (040430Eh)
004084F0 83 C4 04 add esp,4
break;
004084F3 8B E5 mov esp,ebp
004084F5 5D pop ebp
004084F6 C3 ret
case 8:
printf("i == 8\n");
004084F7 68 0C CF 4A 00 push offset string "i == 8\n" (04ACF0Ch)
004084FC E8 0D BE FF FF call _printf (040430Eh)
00408501 83 C4 04 add esp,4
break;
00408504 8B E5 mov esp,ebp
00408506 5D pop ebp
00408507 C3 ret
case 10:
printf("i == 10\n");
00408508 68 18 CF 4A 00 push offset string "i == 10\n" (04ACF18h)
0040850D E8 FC BD FF FF call _printf (040430Eh)
00408512 83 C4 04 add esp,4
break;
00408515 8B E5 mov esp,ebp
00408517 5D pop ebp
00408518 C3 ret
case 35:
printf("i == 35\n");
00408519 68 24 CF 4A 00 push offset string "i == 35\n" (04ACF24h)
0040851E E8 EB BD FF FF call _printf (040430Eh)
00408523 83 C4 04 add esp,4
break;
00408526 8B E5 mov esp,ebp
00408528 5D pop ebp
00408529 C3 ret
;右子树被优化为if else模式
0040852A 83 F8 25 cmp eax,25h
0040852D 74 41 je $LN7+68h (0408570h)
0040852F 3D 9A 02 00 00 cmp eax,29Ah
00408534 74 29 je $LN7+57h (040855Fh)
00408536 3D 10 27 00 00 cmp eax,2710h
0040853B 74 11 je $LN7+46h (040854Eh)
default:
printf("default\n");
0040853D 68 58 CF 4A 00 push offset string "default\n" (04ACF58h)
00408542 E8 C7 BD FF FF call _printf (040430Eh)
00408547 83 C4 04 add esp,4
break;
0040854A 8B E5 mov esp,ebp
0040854C 5D pop ebp
0040854D C3 ret
case 10000:
printf("i == 10000\n");
0040854E 68 48 CF 4A 00 push offset string "i == 10000\n" (04ACF48h)
00408553 E8 B6 BD FF FF call _printf (040430Eh)
00408558 83 C4 04 add esp,4
break;
0040855B 8B E5 mov esp,ebp
0040855D 5D pop ebp
0040855E C3 ret
case 666:
printf("i == 666\n");
0040855F 68 3C CF 4A 00 push offset string "i == 666\n" (04ACF3Ch)
00408564 E8 A5 BD FF FF call _printf (040430Eh)
00408569 83 C4 04 add esp,4
break;
0040856C 8B E5 mov esp,ebp
0040856E 5D pop ebp
0040856F C3 ret
case 37:
printf("i == 37\n");
00408570 68 30 CF 4A 00 push offset string "i == 37\n" (04ACF30h)
00408575 E8 94 BD FF FF call _printf (040430Eh)
0040857A 83 C4 04 add esp,4
break;
0040857D 8B E5 mov esp,ebp
0040857F 5D pop ebp
00408580 C3 ret
;nop
00408581 0F 1F 00 nop dword ptr [eax]
;AddTable First:D5 84 40 00
00408584 D5 84 aadb 84h
00408586 40 inc eax
00408587 00 E6 add dh,ah
00408589 84 40 00 test byte ptr [eax],al
0040858C 3D 85 40 00 3D cmp eax,3D004085h
00408591 85 40 00 test dword ptr [eax],eax
00408594 3D 85 40 00 3D cmp eax,3D004085h
00408599 85 40 00 test dword ptr [eax],eax
0040859C F7 84 40 00 3D 85 40 00 08 85 40 test dword ptr [eax+eax*2+40853D00h],40850800h
004085A7 00 CC add ah,cl