詳解JMP

jmp指令初一看感覺挺簡單,無條件跳轉指令。通常我們通過百度和查閱資料可以瞭解到以下知識

格式: JMP OPRD
 ----

 功能: JMP指令將無條件地控制程序轉移到目的地址去執行.當目的地址仍在同一個代碼段內,稱爲段內轉移;當目標地址不在同一個代碼段內,則稱爲段間轉移.這兩種情況都        將產生不同的指令代碼,以便能正確地生成目的地址,在段內轉移時,指令只要能提供目的地址的段內偏移量即夠了;而在段間轉移時,指令應能提供目的地址的段地址及段內偏移地址值. 


說明: 1. 其中OPRD爲轉移的目的地址.程序轉移到目的地址所指向的指令繼續往下執行.
 ----
       2. 本組指令對標誌位無影響.

       3. <1> 段內直接轉移指令: JMP NEAR 標號
              即: JMP NEAR  標號;  (IP)<--disp16+(IP)
                  JMP SHORT 標號;  (IP)<--disp8+(IP)

          <2> 段內間接轉移指令: JMP OPRD
              例如: JMP BP               ; 轉向(SS):(BP)
                    JMP JNEAR[BX]        ; 轉向(CS):(BX)+JNEAR
                    JMP WORD PTR[BX][DI] ; 轉向(CS):(BX)+(DI)

          <3> 段間直接轉移指令: JMP FAR 標號
              由於標號之前用FAR說明爲遠的屬性,因而只能是一條段間轉移指令.執行該指令時,將把標號所在的段的值送CS,將標號在所屬段內的偏移量送IP,從而形成新的轉移地址CS:IP

          <4> 段間間接轉移指令:JMP OPRD其中的OPRD爲存儲器雙字操作數.段間間接轉移只能通過存儲器操作數來實現.
              例如:指令JMP DWORD PTR[BX],其操作數是一個雙字類型的存儲器操作數,它指向數據段DS,段內偏移爲(BX).從這個DS:BX開始的前兩個字節中,存放了目標地址的段內偏移值,後兩個字節中,存放了目標地址所在的新的段的段基址,分別將它們送至IP及CS,便形成了新的轉移地址.

以上站在一個開發者的角度上來講,應該知道這些足夠了,可是在做逆向分析或inline hook的時候,我們發現在這些還不夠,貌似jmp沒有那麼簡單,先看看一張截圖:

發現了點什麼了沒?jmp的機器碼爲E9,oppd = 跳轉到的地址-當前地址-5 byte

上圖中oppd = A30B0000,當前地址=00C21118,跳轉後的地址爲:00C21CC0

發現 00C21CC0 - 00C21118 - 5 = BA3,並不等於A30B0000哇。可是別忘記了機器碼是按照字節排列的,所以A30B0000實際上是 00000BA3

知道了這個規律之後,我們可以輕鬆的知道,如果oppd爲負(oppd採用補碼,即高位爲1時,也就是oppd最後倒數第二位>9),那麼會向下跳,最高位爲0的時候會向上跳。

在編程應用中,發現很多人喜歡寫成這樣的結構體就見怪不怪啦

typedef struct _JMPCODE

{

 BYTE E9;

 ULONGJMPADDR;

}JMPCODE,*PJMPCODE

可是爲什麼要這樣用那個公式計算出oppd呢,原來在設計jmp指令的時候,用的是偏移,跳到的地址 = 當前地址+oppd+5,那麼就很好理解了,jmp指令,其對應的機器碼中並沒有轉移的目的地址,而是相對於IP的轉移位移,而這個轉移位移在編譯時就已經由編譯器編譯完成。至於說爲什麼有個5字節,因爲jmp指令的長度有2個字節,3個字節和5個字節的說法,在這裏的段間跳轉顯然是 jmp指令長度爲5,所以轉移位移實際上應該是應該是目的地址和jmp下一條指令的位移差咯!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章