要從事「逆向工程」工作,或對其感興趣,請必然要接觸到組合語言。然而我們在對計算機語言的了解大都是從高階語言(如c、vb、dephi)開始的。那當我們閱讀由高階語言翻譯成的機器語言時肯定會遇到很多障礙。下面是我在看了相關書籍後,做的一些筆記,希望有點參考價值!
一、迴圈語句與發彙編後的機器語言
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**與機器反彙編**的關係了!
為了知識的完整性,這裡補充一下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 注釋末尾的反斜槓
今天用gcc編譯遇到如下警告 使用了 wall選項 warning multi line comment wcomment 導致警告的 是乙個函式宣告上面的注釋,如下 this arg is a driver,for example c and you can 查了下原來在注釋行的末尾加上反斜槓會導...
C基礎 strlen與sizeof的區別 彙總
關於 strlen 與 sizeof 的區別,我相信很多人都有所了解。以下我總結了幾點關於strlen 與 sizeof 的區別和注意事項。strlen sizeof 函式運算子 計算字串長度 計算型別所佔記憶體大小 執行結束時才計算出 編譯階段計算 引數只能是char 引數可以是型別也可以是cha...
C 迴圈的巢狀
迴圈巢狀 當迴圈語句中的迴圈體又是乙個迴圈語句時,就構成了 巢狀迴圈 巢狀層次 迴圈的巢狀層次從語法上沒有限制,但一般不超過三層,否則將影響可讀性。應用舉例 例2.16 列印九九表。列印格式為 1 2 3 4 5 6 7 8 9 1 12 2 4 3 3 6 9 9 9 18 27 36 45 54...