前言
JMP 指令根據跳轉目的地遠近分成短跳轉 (Short jump)、近跳轉 (Near jump)、遠跳轉 (Far jump)、他們所代表的含意不一樣,使得機械碼 opcode 呈現的方式也不一樣。
閱讀手冊
引用指令手冊(Intel® 64 and IA-32 Architectures Software Developer's Manual)裡面一些對於 JMP 的敘述:
- Short jump—A near jump where the jump range is limited to –128 to +127 from the current EIP value.
- Near jump—A jump to an instruction within the current code segment (the segment currently pointed to by the CS register), sometimes referred to as an intrasegment jump.
- Far jump—A jump to an instruction located in a different segment than the current code segment but at the same privilege level, sometimes referred to as an intersegment jump.
它們主要是以所要跳的目的地位址與現在的 IP (instruction pointer) 位址差值來區分:
- 短跳轉:差值在 -128 ~ +127 (8-bit) 之內。
- 近跳轉:目的地在同一個區段內,且差值在 32-bit 可以表示的範圍內,又稱段內跳轉。
- 遠跳轉:目的地在別的區段,又稱段間跳轉。
雖然在組合語言都是以 JMP 來實現跳轉,但在組譯轉成 opcode 時翻譯出來的卻不一樣,短跳轉 (EB)、近跳轉 (E9)、遠跳轉 (EA)。當然,opcode 不一樣就代表是不同的指令,其後的參數要怎麼給是該瞭解的事情。參考手冊說明摘要如下:
| Jump | Opcode | Instruction |
|---|---|---|
| Short Jump | EB cb | JMP rel8 |
| Near Jump | E9 cw/cd | JMP rel16/rel32 |
| Far Jump | EA cd/cp | JMP ptr16:16/ptr16:32 |
參數解釋
rel8/rel16/rel32:偏移量(也就是上面提到的差值)能用有號 8/16/32-bit 來表示的相對位址。ptr16:16/ptr16:32:一個相對遠的指標 (pointer),通常與IP不同區段,前面的 16/16 表示 16-bit 的區段暫存器,後面的 16/32 表示目的地在那個區段的偏移量。cb/cw/cd/cp:用來描述代碼的偏移量和新的區段暫存器的值,分別為 1-byte、2-byte、4-byte、6-byte。
程式驗證
有概念之後,用程式來對上面敘述做驗證吧:
短跳轉
| Address | Opcode | Instruction |
|---|---|---|
| 003F1010 | EB 06 | JMP 003F1018 |
| 003F1012 | EB 04 | JMP 003F1018 |
| 003F1014 | EB 02 | JMP 003F1018 |
因為指令位址距目的很近,所以為短跳轉 (EB),EB 後的數字為目的地與 EIP 的偏移量,但可能有人會問,為什麼對不起來?
像是第一個的 偏移量 = 003F1018h - 003F1010h = 08h 結果不是 EB 08,而是 EB 06 。這是因為 CPU 在運作時,看到 EB 06 知道要跳轉,也由於它已經讀了這一指令,EIP 會再加 2 指向下一指令,再執行 EB 06 這個指令。可以看得出關鍵嗎?在執行 EB 06 時 EIP 已經又加 2 了,實際上的算法應為 偏移量 = 003F1018h - (003F1010h + opcode的長度) = 06h
近跳轉
| Address | Opcode | Instruction |
|---|---|---|
| 013D1035 | E9 85 00 00 00 | JMP 013D10BF |
| 013D103A | E9 80 00 00 00 | JMP 013D10BF |
| 013D103F | EB 7E | JMP 013D10BF |
偏移量的算法和短跳轉相同,不同在於近跳轉的 opcode 且 E9 與長度為 5-byte,在計算時要注意。在此特地用跳轉至同目的地但分別為近跳轉與短跳轉來說明,當從 1-byte 的 7E 要進位到 80 時,就會變成 4-byte 的 00 00 00 80h,因為其超出有號 8-bit 可表示範圍。