x86 反彙編/迴圈
為了完成重複的任務,程式設計師通常會實現迴圈。迴圈有很多種類,但它們在彙編程式碼中都可以簡化為幾個相似的格式。本章將討論迴圈,如何識別它們,以及如何將它們“反編譯”回高階表示。
看起來這部分會首先考慮Do-While迴圈,這似乎違反直覺,因為在實踐中,它們可能是所有變體中最少使用的。但是,我們的瘋狂是有方法的,所以請繼續閱讀。
考慮以下通用的 Do-While 迴圈
do
{
action;
} while(condition);
|
![]() |
這個迴圈做什麼?迴圈體簡單地執行,條件在迴圈結束時進行測試,如果條件滿足,則迴圈跳轉回迴圈的開頭。與if語句不同,Do-While條件不會反轉。
現在讓我們看一下以下 C 程式碼
do
{
x++;
} while(x != 10);
它可以被翻譯成這樣的組合語言
mov eax, $x
beginning:
inc eax
cmp eax, 0x0A ;0x0A = 10
jne beginning
mov $x, eax
While迴圈看起來幾乎和Do-While迴圈一樣簡單,但實際上它們並不簡單。讓我們檢查一個通用的 while 迴圈
while(x)
{
//loop body
}
這個迴圈做什麼?首先,迴圈檢查以確保 x 為真。如果 x 不為真,則跳過迴圈。然後執行迴圈體,然後進行另一次檢查:x 仍然為真嗎?如果 x 仍然為真,執行跳轉回迴圈頂部,繼續執行。請記住,在迴圈底部需要有一個跳轉(返回迴圈頂部),但在條件被發現為假的情況下,跳回頂部、重新測試條件,然後再跳回迴圈底部是沒有意義的。while 迴圈然後執行以下步驟
- 檢查條件。如果為假,則轉到末尾
- 執行迴圈體
- 檢查條件,如果為真,則跳轉到步驟 2。
- 如果條件不為真,則從迴圈末尾穿透。
以下是在 C 程式碼中的 while 迴圈
while(x <= 10)
{
x++;
}
下面是同一個迴圈翻譯成彙編程式碼
mov eax, $x
cmp eax, 0x0A
jg end
beginning:
inc eax
cmp eax, 0x0A
jle beginning
end:
如果我們將該彙編程式碼反翻譯回 C,我們將得到以下程式碼
if(x <= 10) //remember: in If statements, we reverse the condition from the asm
{
do
{
x++;
} while(x <= 10)
}
看到我們為什麼先介紹 Do-While 迴圈了嗎?因為當 While 迴圈被彙編時,它變成了 Do-While。
那麼為什麼跳轉標籤不能出現在測試之前呢?
mov eax, $x
beginning:
cmp eax, 0x0A
jg end
inc eax
jmp beginning
end:
mov $x, eax
什麼是 For 迴圈?從本質上講,它是一個帶有初始狀態、條件和迭代指令的 While 迴圈。例如,以下通用的 For 迴圈
for(initialization; condition; increment)
{
action
}
|
![]() |
被翻譯成以下虛擬碼 while 迴圈
initialization;
while(condition)
{
action;
increment;
}
這反過來又會被翻譯成以下 Do-While 迴圈
initialization;
if(condition)
{
do
{
action;
increment;
} while(condition);
}
請注意,在 for() 迴圈中,你通常在 A 中分配一個初始常量值(例如 x = 0),然後將該值與 B 中的另一個常量進行比較(例如 x < 10)。大多數最佳化編譯器將能夠注意到 x 第一次確實小於 10,因此不需要初始 if(B) 語句。在這種情況下,編譯器將簡單地生成以下序列
initialization;
do
{
action
increment;
} while(condition);
使程式碼與 while() 迴圈無法區分。
C 只有 Do-While、While 和 For 迴圈,但其他一些語言很可能實現了它們自己的型別。此外,一個優秀的 C 程式設計師可以很容易地使用一系列好的宏來“自制”一種新的迴圈型別,因此它們值得考慮。
一個常見的 Do-Until 迴圈將採用以下形式
do
{
//loop body
} until(x);
它本質上變成了以下 Do-While 迴圈
do
{
//loop body
} while(!x);
與 Do-Until 迴圈一樣,標準的 Until 迴圈看起來像這樣
until(x)
{
//loop body
}
它(同樣)被翻譯成以下 While 迴圈
while(!x)
{
//loop body
}
Do-Forever 迴圈只是一個沒有限定條件的迴圈,它的條件始終為真。例如,以下虛擬碼
doforever
{
//loop body
}
將變成以下 while 迴圈
while(1)
{
//loop body
}
這實際上可以簡化為一個簡單的無條件跳轉語句
beginning:
;loop body
jmp beginning
請注意,一些非最佳化編譯器將為此生成無意義的程式碼
mov ax, 1
cmp ax, 1
jne loopend
beginning:
;loop body
cmp ax, 1
je beginning
loopend:
請注意,這裡很多比較是不必要的,因為條件是一個常量。大多數編譯器會最佳化這種情況。

