国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

逆向隨筆 - switch 語句深入分析

2019-11-08 03:12:29
字體:
來源:轉載
供稿:網友

switch case 語句在c語言里還是比較簡單的,但是被編譯出來之后,優化結果往往讓人很疑惑,完全看不懂,下面我們一次次的嘗試,看看編譯器到底把switch語句變成什么樣了。

 

① 先上個最簡單的:

 

switch ( argc ) { case 10:  PRintf("case 10 ! /r/n");  break; case 11:  printf("case 11 ! /r/n");  break; default:  printf("default ! /r/n");  break; } getchar();

丟進OD里,看下反匯編代碼:

 

第三行開始,取值到eax中

eax -= 10 ( 0xA )

if (  eax == 0  )                // 如果 eax - 10 == 0,直接可以得出結論 eax == 10

  je 0x002C103E

else

{

   eax--;

   if ( eax == 0 )               // 剛上面 eax - 10 了,這里又減 1,一起就是 如果 eax - 11 == 0, 那么 eax == 11

       je 0x002C1026

   else

       default

}

 

只有少數分支且case的值連續的時候,會用被判斷的值 - 最小值,然后 dec 減1,je 判斷

 

 

② 少數分支,但值不連續的時候

 

switch ( argc )	{	case 10:		printf("case 10 ! /r/n");		break;	case 100:		printf("case 100 ! /r/n");		break;	default:		printf("default ! /r/n");		break;	}	getchar();

反匯編:

 

我們可以看到,直接就是cmp , je,類似于 if else 結構

 

只有少數分支,case的值不連續的時候,直接cmp , je

 

 

③ 當分支數量大于3個且連續的時候

 

 

switch ( argc )	{	case 10:		printf("case 10 ! /r/n");		break;	case 11:		printf("case 11 ! /r/n");		break;	case 12:		printf("case 12 ! /r/n");		break;	case 13:		printf("case 13 ! /r/n");		break;	default:		printf("default ! /r/n");		break;	}	getchar();

反匯編:

 

依舊是第三行開始,這次貌似代碼不太一樣了,沒錯,這又是一個新姿勢了

 

eax = eax - 10 ( 0xA )

cmp eax, 3 這里是什么意思呢??? 為什么突然跟3比較?為嘛不是 4,5,6 ? 原來,這個時候為了達到更好的性能,編譯器替我們生成了一張表,跳轉表,這也是switch語句的精髓所在

大跳轉表(為嘛叫大表,后面解釋),其實就是一個地址數組

    下標范圍:case最大值 - case最小值

    大?。篶ase最大值 - case最小值 + 1

 

我們看后面的尋址方式,jmp dWord ptr ds:[ eax*4 + 0xFC1090 ],典型的數組尋址,這個0xFC1090就是跳轉表首地址,我們看看這個表,上圖紅色選中部分,我們發現里面存儲的值剛好是case的地址,我們理清下思路:

 

值 - case中的最小值 得到大表的索引,如果這個索引不在大表下標范圍內,ja (無符號大于跳轉)到 default,否則,jmp dword ptr ds:[ eax*4 + 0xFC1090 ],用這個索引在大表中取得 case 對應地址,直接過去。

 

這個大表是編譯器生成,我們不用去管,至于怎么生成?大家可以自己來嘗試實現一下。

 

 

④ 當分支數量大于3個且部分不連續的時候(差值較?。?/strong>

 

switch ( argc )	{	case 10:		printf("case 10 ! /r/n");		break;	case 11:		printf("case 11 ! /r/n");		break;	case 13:		printf("case 13 ! /r/n");		break;	case 15:		printf("case 12 ! /r/n");		break;	default:		printf("default ! /r/n");		break;	}	getchar();

反匯編:

 

 

大表大?。?15 - 10 + 1 = 6 ,這個時候我們也只case了4個值,但是大表仍然被補齊成6個了,觀察發現,中間缺少的值被補成default

當我們的值為14時,14 - 0xA = 4,  4 < 5,  [ 4*4 + 0xFC1090 ] =  0x00041075 -> default,是不是很機智。

 

 

 

⑤ 當分支數量大于3個且部分不連續的時候(差值較大)

 

switch ( argc )	{	case 10:		printf("case 10 ! /r/n");		break;	case 11:		printf("case 11 ! /r/n");		break;	case 12:		printf("case 12 ! /r/n");		break;	case 19:		printf("case 19 ! /r/n");		break;	default:		printf("default ! /r/n");		break;	}	getchar();

 

反匯編:

 

這個時候我們的兩個值的差值是7,這個時候發現又不一樣了,尋址方式變成了:movzx eax, byte ptr ds:[ eax + 0xE610A8 ],dword 變 byte 了,我們數據窗口中看一下,如圖選中內容,這就是小表,每個元素只占1個字節,小表大小也是最大值 19  - 最小值 10,接下來就是 jmp dword ptr ds:[ eax*4 + 0xE61094 ],這個當然,又是我們親愛的大表了,聯系上下文我們發現,小表里面存的就是大表的下標,為什么要這樣設計呢? 因為大表占四個字節,當差距比較大時,生成的大表自然也會變得很大,這個時候使用小表,可以更加節約內存。

 

 

⑥ 當分支數量大于3個且部分不連續的時候(差值非常大)

 

switch ( argc )	{	case 10:		printf("case 10 ! /r/n");		break;	case 11:		printf("case 11 ! /r/n");		break;	case 12:		printf("case 12 ! /r/n");		break;	case 600:		printf("case 600 ! /r/n");		break;	default:		printf("default ! /r/n");		break;	}	getchar();

 

反匯編:

 

最大值 600 - 最小值 10 = 590,,小表 590 字節 ? 這樣的話,小表就很大了,所以,又進行了改變,連續的部分依然使用第一種方法,不連續的使用 if else 結構,不再使用跳轉表了。

 

 

通過對 switch case 的一步步分析,我們發現情況還是很多的,可能不同的編譯器不一樣的優化,搞清楚原理,才能真正游刃有余。

 

 

 

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 土默特右旗| 洞口县| 青州市| 呈贡县| 海盐县| 杭锦后旗| 安平县| 郴州市| 高青县| 江永县| 临泉县| 桓台县| 阳高县| 特克斯县| 昆明市| 蓬溪县| 晴隆县| 田阳县| 颍上县| 增城市| 神池县| 益阳市| 安国市| 随州市| 赞皇县| 永康市| 道孚县| 板桥市| 武安市| 徐汇区| 新余市| 汝城县| 辉南县| 建阳市| 五大连池市| 瓦房店市| 罗甸县| 阿拉尔市| 建宁县| 竹山县| 遵义县|