switch語句反彙編分析

2021-06-26 02:47:01 字數 3366 閱讀 1954

switch語句如下:

反彙編**為:

將區域性變數flag存放現在棧底即

[ebp-8]

,然後通過

eax做中轉,在

[ebp-4]

存放乙個

flag

的備份,複製到

ecx中對其進行減一操作,其中的1則是

case

分支中的最小值,然後將操作完的數儲存到

[ebp-4]

中,此時為

flag-1=2

,接著進行

cmp操作,比較的數值4是由

case

分支的最大值和最小值相減得到的,如果

flag-1

大於case

分支的相差值,則跳轉到位址

0x0129107c

處,此位址為

default

語句位址。

此處的演算法為將flag

減去最小的分支得到乙個數,然後和

case

分支最大值與最小值相差數值比較,即是比較兩個區間,如果第乙個區間較大說明

flag

不在case

分支中則跳轉到

default

分支,但如果比

case

分支的最小值小呢?怎麼沒比較?這裡其實可以採用乙個語句即

if(flag>case

最大值 

|| flag最小值

) 跳轉到default

,但是編譯器這裡使用到了乙個技巧,如果

falg

比case

值小,flag

減去最小的

case

值會變成負數,而負數在記憶體中以補碼形式存放的,使用

ja指令是比較的無符號數,因此也就肯定比那個區間值大了,(例如

falg=1

,case

最小值為2,則

1-2=-1

,在記憶體中存放為

ffffffffh

,是乙個很大的數)很不錯的技巧!

如果flag

在case

區間的話就會將

flag-1

的值傳遞給

edx,然後通過

[edx*4+01291094h]

定位到case

分支的位址,為什麼可以通過

edx定位呢?

01291094h

又是乙個什麼位址?其實編譯器事先分配好了乙個位址陣列,其中存放的則是

case

分支的位址,

01291094h

是這個陣列的首位址,

edx是作為索引來找到分支位址的,這也是

switch

語句不能使用浮點數作為

flag

的原因。程式中此時

edx為

2,也就是去

[2*4+01291094h]

即[0129109ch]

中尋找case3

的位址,為

0129104ch

如果flag

不在case

分支中但在其區間內會怎麼樣呢?現在假設

flag還是3

,但去掉

case3

的語句,這時以

edx為索引跳過去的位址中存放的是

default

的位址。觀察發現在編譯器生成的位址陣列中存在

max-min+1

個單元,其中僅有

case

值為索引存放的才是

case

分支的位址,其餘全是

default

的位址。

如果switch

語句只有兩個分支,即

case1

和case100

那豈不是要建立長度為

100的位址陣列,只有頭和尾是分支位址,其餘全是

default

位址,極大地浪費記憶體空間,反編譯可以發現編譯器採用了

if語句的格式來進行跳轉

編譯器在對case分支有三個及以下的時候,三個

if語句也就是三條減法(

cmp),而位址陣列則需要兩條減法(

sub、

cmp)和乙個索引定位的乘法(至少相當於一次減法),速度並不佔優勢,因此編譯器會選擇

if語句來實現。但是即使程式中有

4個分支,有時候編譯器也會採用

if語句,假如說是

case1,case2,case3,case10000,

編譯器的位址跳轉表將會變得很大,此時也會採用

if語句的形式。

switch語句還有一種情況就是

case

分支很多,但是每個分支的數值比較稀疏的情況,跳轉表元素為

50-40+1=41

個,但只有

5個元素是分支的位址,其餘全是

default

的位址,此時採用跳轉表也會造成太多的記憶體浪費,編譯器對這種情況進行了優化

在這裡將[edx+address]中存放的乙個位元組傳送到

eax中,其餘位補

0,那這個

address

是什麼位址

?定位到

address

可以看到其中存放的資料為:

假設flag=10

的話,先從

[0+010110b4h]

處取出乙個位元組00給

eax,然後

eax充當索引到另乙個位址中取出內容跳轉,終會跳轉到

0101109ch

處取位址

此處的位址正好為case10處的位址

假設flag=15的話,先從

[5+010110b4h]

處取出位元組

05傳遞給

eax,然後通過

eax的索引可以跳轉到

5*4+0101109ch=010110b0h

處取位址

此處的位址為default的位址

通過以上的實驗可以看出switch

語句在case

分支較稀疏的情況下會採用二次跳轉的方式來進行優化,第乙個陣列存放第二個陣列的索引號,元素個數為

max-min+1

(max

、min

為case

分支最大最小值),元素大小為乙個位元組,第二個陣列根據索引號找到存放的

case

分支位址,第二個陣列的大小為

case

分支的個數+1(

default

分支)。上面的實驗中如果不採用優化的話跳轉表的大小為(

50-10+1

)*4=164

位元組,而優化後為(

50-10+1)*1+(5+1)*4=65位元組,大大節省了記憶體空間,但是需要進行兩次陣列訪問,典型的時間換空間。

switch語句反彙編分析

假設switch語句的分支比較少的時候 例如3,少於4的時候沒有意義 沒有必要使用此結構,相當於if 可觀察反彙編得出此結論 1 各個分支常量的差值較大的時候,編譯器會在效率還是記憶體進行取捨,這個時候編譯器還是會編譯成類似於if,else的結構。2 在分支比較多的時候 在編譯的時候會生成乙個大表 ...

C 反彙編四 SWITCH語句

004015d0 push ebp 004015d1 mov ebp,esp 004015d3 sub esp,48h 004015d6 push ebx 004015d7 push esi 004015d8 push edi 004015d9 lea edi,ebp 48h 004015dc mo...

C 反彙編 if語句分析

include void main std cout hello world 7?1 4 if b 1 std cout b equal 1 判斷語句有幾種 1.條件表示式 表示式1?表示式2 表示式3 2.if語句 3.switch語句 首先來分析if語句吧 debug版 0040117d 68 ...