跳轉到內容

x86 反彙編/迴圈示例

來自華夏公益教科書,開放的書籍,為開放的世界

示例:識別目的

[編輯 | 編輯原始碼]

這個函式的功能是什麼?它接受什麼型別的引數,並且它返回什麼型別的結果(如果有)?

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

該函式迴圈遍歷一個由 esi 指向的 4 位元組整數陣列,並將每個條目相加。它在 eax 中返回總和。唯一的引數(位於 [ebp + 8] 中)是指向整數陣列的指標。ebx 和 100 之間的比較表明輸入陣列包含 100 個條目。指標偏移量 [esi + ebx * 4] 表明陣列中的每個條目都是 4 位元組寬的。

示例:完整的 C 原型

[編輯 | 編輯原始碼]

這個函式的 C 原型是什麼?確保包含引數、返回值和呼叫約定。

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

請注意 **ret** 函式如何從堆疊中清除其引數?這意味著該函式是一個 STDCALL 函式。我們知道該函式以指向整數陣列的指標作為其唯一引數。然而,我們不知道這些整數是有符號的還是無符號的,因為 **je** 命令用於兩種型別的值。我們可以假設其中一個,為了簡單起見,我們可以假設無符號值(在這個函式中,無符號值和有符號值實際上將以相同的方式工作)。我們還知道返回值是一個 4 位元組整數,與引數陣列中找到的型別相同。由於該函式沒有名稱,我們可以將其簡單地稱為 “MyFunction”,並且我們可以將引數稱為 “array”,因為它是一個數組。從這些資訊中,我們可以確定 C 中的以下原型

 unsigned int STDCALL MyFunction(unsigned int *array);

示例:反編譯成 C 程式碼

[編輯 | 編輯原始碼]

將此程式碼反編譯成等效的 C 原始碼。

 push ebp
 mov ebp, esp
 mov esi, [ebp + 8]
 mov ebx, 0
 mov eax, 0
 mov ecx, 0
 _Label_1:
 mov ecx, [esi + ebx * 4]
 add eax, ecx
 inc ebx
 cmp ebx, 100
 jne _Label_1
 mov esp, ebp
 pop ebp
 ret 4

從上面的函式原型和對該函式功能的描述開始,我們可以開始為該函式編寫 C 程式碼。我們知道該函式在迴圈之前初始化了 eax、ebx 和 ecx。但是,我們可以看到 ecx 只是用作中間儲存位置,從陣列中接收連續的值,然後將其新增到 eax。

我們將建立兩個無符號整數,a(用於 eax)和 b(用於 ebx)。我們將在 **register** 限定符的幫助下定義 a 和 b,這樣我們就可以指示編譯器不要在堆疊上為它們建立空間。對於每個迴圈迭代,我們都將陣列在位置 ebx*4 處的值新增到執行總和 eax。將此轉換為我們的 a 和 b 變數,並使用 C 語法,我們看到

 a = a + array[b];

迴圈可以是 **for** 迴圈或 **while** 迴圈。我們看到迴圈控制變數 b 在迴圈之前初始化為 0,並且在每次迴圈迭代中遞增 1。迴圈在 *遞增後* 對 b 進行 100 的測試,因此我們知道 b 在迴圈主體內部從不等於 100。利用這些簡單的事實,我們將以 3 種不同的方式編寫迴圈

首先,使用 **while** 迴圈。

 unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b = 0;
    register unsigned int a = 0;
    while(b != 100)
    {
       a = a + array[b];
       b++;
    }
    return a;
 }

或者,使用 **for** 迴圈

unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b;
    register unsigned int a = 0;
    for(b = 0; b != 100; b++)
    {
       a = a + array[b];
    }
    return a;
 }

最後,使用 **do-while** 迴圈

unsigned int STDCALL MyFunction(unsigned int *array)
 {
    register unsigned int b = 0;
    register unsigned int a = 0;
    do
    {
       a = a + array[b];
       b++;
    }while(b != 100);
    return a;
 }
華夏公益教科書