彙編學習--函數

函數是一個程序模塊,用來實現一個特定的功能。一個函數包括函數名、入口參數、返回值、函數功能等功能。

一、函數的識別

程序通過調用程序來調用函數,在函數執行後又返回調用程序繼續執行。調用函數的代碼中保存了一個返回地址,該地址會與參數一起傳遞給被調用的函數。有多種方法可以實現這個功能,在絕大多數情況下,編譯器會使用call和ret指令來調用函數及返回調用函數。
call指令:call指令與跳轉指令功能相似,不同的是,call指令保存返回信息,即將其之後的指令地址壓入棧的頂部,當遇到ret指令時返回這個地址。
因此,可以通過定位call機器指令或利用ret指令結束的標誌來識別函數。call的指令的操作數就是所調用函數的首地址。
可以先看一個示例:
在這裏插入圖片描述
在這裏插入圖片描述這是函數直接調用的方式,大部分情況也是這樣;不過也有調用函數是間接調用的,即通過寄存器函數地址或動態計算函數地址調用。例如:

call [4*eax+10h]

二、函數的參數

函數傳參有三種方式:分別是棧方式、寄存器以及全局變量進行隱含參數傳遞的方式。若是棧傳遞的,就需要定義參數在棧中的順序,並約定函數被調用後由誰來平衡棧。若是通過寄存器傳遞,就要確定參數放在那個寄存器中。

(1)利用棧傳參
首先看一個示例:

// t1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
int WINAPI test1(int a,int b)
{
     return a+b;
}
int __fastcall test2(int a,int b)
{
            return a+b;
}
int PASCAL test3(int a,int b)
{
   return a+b;
}
int __stdcall test4(int a,int b)
{
 
            return a+b;
}
int test5(int a,int b)
{
            return a+b;
}

int main(int argc, char* argv[])
{
            test1(3,5);
            test2(3,5);
            test3(3,5);
            test4(3,5);
            test5(3,5);

        return 0;
}

切入到反彙編可以看到函數的調用過程,接下來我們重點看一看每一種方式去調用函數時,函數內部是按照什麼順序將參數壓棧,以及函數結束後,由誰來平衡棧?
在這裏插入圖片描述

1.WINAPI

參數傳遞時,先傳遞3,然後在與5相加;在子程序執行時最後一句 ret 8,用於平衡堆棧。
在這裏插入圖片描述

2.pascal
此處參數傳遞時,先將3給eax,然後與5相加,同樣有個 ret 8 ,也是由子程序平衡堆棧。
在這裏插入圖片描述
3._stdcall
此處參數傳遞時,先將3給eax,然後與5相加,同樣有個 ret 8 ,也是由子程序平衡堆棧。

在這裏插入圖片描述
4._cdecl(c規範)

看到函數的調用過程都是先參數壓棧,在調用函數時,多了一句 add esp,8 ,說明_cdecl需要調用者平衡堆棧。

在這裏插入圖片描述
小結:
經過自己的實踐,與書上做對比,平衡棧者是吻合了,
不過參數傳遞順序卻發現都是先push 5,然後push 3,按照這樣的話,那都是從右到左,這與書上並不符合,這是讓我困惑的一個點。
在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述
可是看了這張圖之後,發現在VC裏面pascal方式就是stdcall方式,也就是winapi方式,那我之前的疑惑在這裏也算是解決了。

(2)利用寄存器傳遞參數

這裏主要涉及fastcall規範和thiscall規範

1.fastcall規範:顧名思義,特點就是快,因爲它是靠寄存器來傳遞參數的。

因爲可以看到這裏的調用方式是push 9,push 7, mov edx,5,mov ecx,3,也就是把左邊兩個參數分別放在了ecx和edx寄存器中,然後後兩個參數按照從右向左的順序放入了棧中,所以這裏的參數傳遞時,使用寄存器和棧,在子程序執行時,先把寄存器的兩個數放入棧中,然後進行計算,最後是 ret 8,用於平衡堆棧。
在這裏插入圖片描述在這裏插入圖片描述

2.thiscall規範:thiscall是c++的非靜態類成員函數的默認調用約定,對象的每個函數隱含接收this函數。採用thiscall函數時,函數約定的參數按照從右到左的順序入棧,被調用的函數在返回前清理傳送參數的棧,僅通過ecx寄存器傳遞一個額外的參數-----this指針。

三、函數的返回值

(1)用return操作符返回值
在一般情況下,函數的返回值放在eax寄存器中返回,如果處理結果的大小超過eax寄存器的容量,其高32位就會放到edx寄存器裏。

(2)通過參數按傳引用方式返回值
給函數傳參有兩種,分別是傳值和傳引用。

傳值調用時,會建立參數的一份複本,並把它傳給調用函數,在調用函數中修改參數值的複本不會影響參數的原始值。

傳引用調用允許調用函數修改原始變量的值.調用某個函數,當把變量的地址傳遞給函數時,可以在函數中用間接引用運算符修改調用函數內存單元中該變量的值。

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