在使用高級語言例如java,C++,python來編寫代碼時,咱們使用最多的莫過於分支跳轉控制語句,例如if..else, switch..case, for()等,本節咱們看看這些分支跳轉語句如何在X86彙編語言下呈現。
程序其實由一系列控制流組成,當代碼運行時若是某個條件知足,它會執行既定代碼,若是不知足或者另外一個條件知足,它又會執行另外一部分代碼。在控制跳轉指令中,最經常使用的就是jmp指令,它讓控制流直接跳轉到具體設定的位置去執行那裏的代碼。這種跳轉因爲無需判斷先決條件,所以也叫無條件跳轉。
問題在於程序在邏輯設計上一般須要知足固定條件的跳轉,例如提到的if..else就屬於這種類型。在彙編語言層面就須要使用標誌位來進行跳轉前的條件判斷。在彙編語言層面能夠實現多達三十多種的條件跳轉方式。咱們先看幾個例子:
jz addr #當標誌寄存器上的ZF位設置成1時跳轉到addr指定的地址
jnz addr #當標誌位ZF爲0時跳轉到地址addr
je addr #常常跟在cmp指令後,cmp指令用於比對兩個操做數是否相等,若是相等就將ZF標誌位設置成1,因而je就跳轉到地址addr
jne addr #使用cmp比對兩個操做數,若是兩個數值不相等則跳轉到地址addr
jg addr #使用cmp比對兩個有符號的操做數,若是第一個操做數比第二個大,那麼跳轉到地址addr
jng addr #使用cmp比對兩個有符號的操做數,若是第一個不大於第二個則跳轉到地址addr。
ja,jae 分別對應jg和jge,不一樣在於這兩條指令比對的是無符號操做數
jl addr #使用cmp比對兩個有符號操做數,若是第一個比第二個小,那麼跳轉到地址addr
jle #使用cmp指令比對兩個有符號操做數,若是第一個不小於第二個,那麼跳轉到地址addr
jb, jbe做用於je,jle相同,不過比較的是無符號操做數
jo addr #若是該指令的上一條指令運行後時代標誌位OF設置爲1則跳轉到地址addr
js addr #若是上一條指令運行後使得標誌位SF設置爲1則跳轉到地址addr
jecxz addr #若是寄存器ECX的值被設置爲0則跳轉到地址addr
在程序設計時還須要常常使用for , while等循環語句,這些循環功能其實就是使用了上面描述的跳轉指令來實現,只要判斷某個條件是否成立,若是成立則經過jmp跳轉回原來指令的起始位置就能實現對同一部分指令進行屢次運行的效果。
在X86彙編語言中,還有一系列指令專門負責對數據進行批量操做。例如指令movsx, cmpsx, stosx, scasx 其中x對應字符b或w,若是對應b那麼對應指令就是對批量的字節數據,也就是8位數值進行操做,若是對應w則對字數據,也就是16位數值進行批量操做。這些指令在執行時會改變寄存器ESI和EDI的值,ESI指向數據的源地址,EDI指向數據的目的地址,同時寄存器ECX用於計數。
假設在地址0x8000處存有一個含有10字節個元素的數值,代碼想將它們複製到地址0x9000,那麼對應的彙編指令相似下面:
mov esi, 0x8000
mov edi, 0x9000
mov ecx, 10
rep movsb
rep指令告訴CPU重複執行指令movsb,每執行一次,寄存器ECX的值就減去1,直到它的值爲0爲止。movsb將esi處一字節的數據轉移到edi對應的地址,而後分別把這兩個寄存器的數值增長1。若是你對C語言熟悉的話,你會想到函數memcpy。
在上面代碼的執行過程當中還受到一個標誌位DF的影響,若是DF的值位0,那麼每次執行movsb後,esi,edi的值就會增長1,若是DF的值位1,那麼esi,edi的值在每次指令movsb執行後就會減1.
在前面代碼中,若是指令mvsb變成cmpsb,那麼CPU會將edi指向內存地址所存儲的數值減去esi所指向內存地址的數值,而後根據結果來設置標誌位。cmpsb則比對esi,edi兩處內存存儲的數值直到兩個數值相等或者ecx寄存器的數值減爲0爲止。scasb指令用於搜索esi指定位置的數值,而後與寄存器al中的數值進行比對,若是相等則這種相關標誌位,而後中止或者是將ecx寄存器的值減到0後中止。
彙編語言是黑客技術中很是複雜,繁瑣,反人性的一部分,絕大多數人都會見而遠之,這也是不少人沒法掌握底層技術原理的緣由。這幾節介紹的彙編語言僅僅是一點點皮毛,剩下的還須要渴望掌握黑客技術或是但願掌握底層原理的同窗本身去學習和把握。
本文分享自微信公衆號 - Coding迪斯尼(gh_c9f933e7765d)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。java