看雪一同志寫的文章,我覺得很基礎和有用,知識著眼於小處。積累才是王道!
一、迴圈語句與發彙編後的機器語言
1、for迴圈
下面是一段c語言的**,我們的目的是來看其反彙編的結果:
int myfunction(int a,int b)
return c;
} 前面的反彙編暫時不理它,這裡從for的地方開始反彙編,結果如下:
for(i=0;i<50;i++)
00412bc7 mov dword ptr [i],0 // i=0; 給迴圈變數賦初值
00412bce jmp myfunction+39h (412bd9h)// 跳到第一次迴圈處
> 00412bd0 mov eax,dword ptr [i]
| 00412bd3 add eax,1 // i++;修改迴圈變數
| 00412bd6 mov dword ptr [i],eax
| 00412bd9 cmp dword ptr [i],32h // 比較 i 與50的關係, 檢查迴圈條件
| 00412bdd jge myfunction+4ah (412beah) // 當 i>=50 [即 !(i<50) ] 時則跳出迴圈
|
< 00412be8 jmp myfunction+30h (412bd0h) // 跳回去修改迴圈變數
00412bea mov eax,dword ptr [c]
}可以看到for迴圈主要用這麼幾條指令來實現:mov進行初始化。jmp跳過迴圈變數改變**。cmp實現條件判斷,jge根據條件跳轉。
用jmp回到迴圈改變**進行下一次迴圈。所以for結構有以下的顯著特徵:
mov 《迴圈變數》,《初始值》 ; 給迴圈變數賦初值
jmp b ;跳到第一次迴圈處
a: (改動迴圈變數) ;修改迴圈變數。
… b: cmp 《迴圈變數》,《限制變數》 ;檢查迴圈條件
jgp 跳出迴圈
(迴圈體)
…
jmp a ;跳回去修改迴圈變數
2、do迴圈
再看一下do迴圈,因為 do迴圈沒有修改迴圈變數的部分,所以比for迴圈要簡單一些。
do while(c< 100);
00411a5e cmp dword ptr [c],64h
00411a62 jl myfunction+35h (411a55h)
return c;
do迴圈就是乙個簡單的條件跳轉回去。只有兩條指令:
cmp 《迴圈變數》,《限制變數》
jl 《迴圈開始點》
3、while迴圈
while(c<100)
00411a64 jmp myfunction+35h (411a55h)
return c;
很明顯,我們會發現while要更複雜一點。因為while除了開始的時候判斷迴圈條件之外,後面還必須有一條無條件跳轉回到迴圈開始的地方,共用三條指令實現:
a: cmp 《迴圈變數》,《限制變數》
jge b
( 迴圈體)
… jmp a
b: (迴圈結束了)
這樣,我們對c語言中的迴圈結構的分析,就基本弄完了!當然,我們用同樣的方法也可以分析出c語言在「分支語句」、以及其它的資料結構中的c**與機器反彙編**的關係了!
二、分支語句
1、if-else 語句
為了觀察其彙編語句,下面是乙個簡單的if判斷結構:
if(a>0 && a<10)
else if( a>10 && a<100)
else
if 判斷都是使用cmp再加上條件跳轉指令。對於if( a && b)的情況,一般都是使用否決法。如果a不成立,立刻跳下乙個分支。依次,如果 b 不成立,同樣跳下一分支。
cmp 條件
jle 下乙個分支
所以開始部分的反彙編為:
if(a>0 && a<10)
00411a66 cmp dword ptr [c],0
00411a6a jle 411a81h ; 跳下乙個else if的判斷點
00411a6c cmp dword ptr [c],0ah
00411a70 jge 411a81h ; 跳下乙個else if的判斷點
else if 的和 else 的特點是,開始都有一條無條件跳轉到判斷結束處,阻止前面的分支執行結束後,直接進入這個分支。這個分支能執行到的唯一途徑只是,前面的判斷條件不滿足。
else 則在jmp之後直接執行操作。而else if則開始重複if之後的操作,用cmp比較,然後用條件跳轉指令時行跳轉。
else if( a>10 && a<100)
00411a7f jmp 411aa9h ;直接跳到判斷塊外
00411a81 cmp dword ptr [c],0ah ;比較+條件跳轉,目標為下乙個分支處
00411a85 jle 411a9ch
00411a87 cmp dword ptr [c],64h
00411a8b jge 411a9ch
else
00411a9a jmp 411aa9h ;這裡是else,所以只有簡單的一條跳轉。
return c;
2、switch-case 語句
switch 的特點是有多個判斷。因為 swtich 顯然不用判斷大於小於,所以都是je(因此,c語言中switch語句不支援float型別的變數),分別跳到每個case處。最後乙個是無條件跳轉,直接跳到default處。以下的**:
switch(a)
default:
printf("a>10 && a<100");
} 反彙編的switch(a)
00411a66 mov eax,dword ptr [a]
00411a69 mov dword ptr [ebp-0e8h],eax
00411a6f cmp dword ptr [ebp-0e8h],0 // case 0:
00411a76 je 411a83h
00411a78 cmp dword ptr [ebp-0e8h],1 // case 1:
00411a7f je 411a90h
00411a81 jmp 411a9fh // default:
之外。從這裡我們可以發現:switch語句裡,完成「比較判斷」的指令會與「case」指令的兩部分,在彙編中,不是按照c語句逐句翻譯的,而是分開為兩個指令模組來實現的!
case 0:
printf("a>0");
00411a83 push offset string "a>0" (4240dch)
00411a88 call @ilt+1300(_printf) (411519h)
00411a8d add esp,4
case 1:
default:
printf("a>10 && a<100");
00411a9f push offset string "a>10 && c<100" (424288h)
00411aa4 call @ilt+1300(_printf) (411519h)
00411aa9 add esp,4
} 至於case 和 default分支中,如果有break,則會增加乙個無條件跳轉彙編指令。若沒有break,則就沒有任何迴圈控制**。
小結:如果在反彙編**中發現連續多個「比較cmp」和「相等跳轉je」就會讓人聯想到「switch」語句了!
從彙編來看c語言
一 學習過程從c 語言的角度提出一些問題,這些問題再從彙編的角度考慮,還真的很有意思。1 我們用高階語言程式設計時,一般不可能不用到變數,但是一定要用到變數嗎?還有這些變數從彙編的角度是怎麼實現的呢?畢竟暫存器和記憶體空間是不可能儲存乙個變化的值的,要改變暫存器和記憶體空間的值,只有重新賦值。但是原...
從機器語言到組合語言
機器語言是計算機唯一能接受和執行的語言。機器語言由二進位製碼組成,每一串二進位製碼叫做一條指令。一條指令規定了計算機執行的乙個動作。一台計算機所能懂得的指令的全體,叫做這個計算機的指令系統。不同型號的計算機的指令系統不同。指令通常由幾個位元組組成,第乙個位元組是操作碼,它規定了計算機要執行的基本操作...
從C語言到C 語言
c 讀作 c加加 是 c plus plus 的簡稱。顧名思義,c 是在c語言 的基礎上增加新特性。從語法上看,c語言是 c 的一部分,c語言 幾乎不用修改就能夠以 c 的方式編譯。對於c和c 的關係,有很多種說法,新人很容易認為c 是c的公升級版,c 比c要高階,c 高大上,c過時了,這種想法不一...