[C++反彙編] 呼叫約定 cdecl stdcall fastcall 等
該系列文章是依據本人平時對反彙編的學習,歸納總結,所做的學習筆記。如有錯誤或待改善之處,請留下您寶貴的意見或建議。
呼叫約定是指呼叫方放置函式呼叫所需的引數的具體位置。具體的位置可以是特定的暫存器、程式棧、亦或者暫存器和棧中。呼叫約定還有一個重要的任務:函式呼叫完成後(被呼叫函式結束後),是誰(呼叫方還是被呼叫方)完成棧平衡工作(也就是清理棧中的引數)。有的呼叫約定由呼叫方完成棧平衡工作(如C呼叫約定、C++呼叫約定(g++)),而有的呼叫由被呼叫方完成棧的平衡工作(如stdcall呼叫約定、fastcall呼叫約定、C++呼叫約定(MS Visual C/C++))。遵守指定的呼叫約定對於維護棧指標的平衡與完整性有重要的作用。下面我們就一一介紹這幾種呼叫約定。其中的例子程式碼來源於《
1. C呼叫約定(cdecl呼叫約定)
這是X86體系結構中,許多C編譯器預設的呼叫約定。在C/C++程式中,常用_cdecl修飾符迫使編譯器使用C呼叫約定。
cdecl呼叫約定的規則是:呼叫方按從右到左的順序將引數壓入棧中,在被呼叫方完成操作後,由呼叫方負責完成棧平衡工作。
引數從右到左入棧的一個好處是:如果函式被呼叫,最左邊的(第一個)引數將始終位於棧頂,這樣,無論函式需要多少個引數,都能輕易的取到第一個引數。因此,cdecl呼叫約定很適合那些引數不定的函式,如printf。
由於需要呼叫方進行棧平衡,所以在函式呼叫返回後,有立即對棧指標進行調整的操作。如果函式的引數是可變的,那麼由呼叫方完成棧平衡工作看起來更為合適,因為呼叫方清楚傳遞了多少個引數,可以輕鬆的做出調整;而被呼叫方無法事先知道接受了多少個引數,很難進行調整。
下面一個例子,用於說明cdecl呼叫約定。
函式的原型為:
void demo_cdecl(int w, int x, int y, int z);
該函式預設情況下使用cdecl呼叫約定,要求從右到左傳遞引數,並且由呼叫方完成棧平衡工作。可能生成的彙編程式碼如下:
;demo_cdecl(1,2,3,4); push 4 ;pushz push 3 ;pushy push 2 ;pushx push 1 ;pushw call demo_cdecl ;callthe function add esp, 16 ;adjustesp to its former value
首先在2-5行,引數從右到左入棧,棧指標(esp)變化了16個位元組(在32位機上,4*sizeof(int) =16)函式返回後,第7行對esp進行了調整。
還有一種方式就是,在函式呼叫之前,編譯器在棧頂預先分配16個位元組的空間,在引數入棧後,並不需要調整棧指標,在函式返回後也不需要調整棧指標。
這正是GUN編譯器(gcc/g++)使用的函式入棧方式,但無論是哪一種方式,棧指標都會指向第一個引數。彙編程式碼如下:
;demo_cdecl(1,2,3,4)
mov [esp+12], 4 ;mov z to the stack
mov [esp+8], 3 ;mov y to the stack
mov [esp+4], 2 ;mov x to the stack
mov [esp], 1 ;mov w to the stack
call demo_cdecl ;call function
2. “標準”呼叫約定(stdcall呼叫約定)
這裡的標準,只是微軟為自己的呼叫約定所起的名稱,而不是傳統意義上的標準。stdcall呼叫約定使用修飾符:_stdcall,如下:
void _stdcalldemo_stdcall(int w, int x, int y, int z);
與cdecl呼叫約定一樣的是,stdcall的呼叫約定也按從右到左的順序傳遞引數;而區別在於,函式執行結束時,由被呼叫的函式負責完成函式棧的平衡工作,但是對於被呼叫的函式而言,要想在執行結束時完成這項工作,必須清楚的知道棧中有多少個引數,所以這隻有在函式接收的引數固定不變時,被呼叫函式才能完成這項工作。因此,想printf這樣的引數可變的函式,不能使用stdcall呼叫約定。
如上,demo_stdcall函式需要4個引數,在棧上共佔用了16個位元組(這是在32位機上4*sizeof(int) =16)。x86編譯器能夠使用RET指令的一種特殊形式,同時從棧頂取出返回地址,並給棧指標加上16,已完成棧的平衡工作,可能的RET這令為:
ret 16 ;returnand clear 16bytes from the stack
stdcall的優點是:在每次函式呼叫之後,不需要通過程式碼從棧中清楚引數,因此能夠生成體積稍小,速度稍快的程式。
根據慣例,微軟對所有由DLL檔案輸出的引數數量固定的函式使用stdcall呼叫約定。
3. X86 fastcall呼叫約定
fastcall約定是stdcall的一種變體。它向CPU暫存器(而非函式棧)傳遞最多兩個引數。MicrosoftVisual C/C++和GNU gcc/g++(3.4及更低版本)編譯器能夠識別函式宣告中的fastcall修飾符。如果指定使用fastcall呼叫約定,則傳遞給函式的前兩個引數分別位於ECX和EDX暫存器中,剩餘的引數則以類似於stdcall呼叫約定的方式從右到左入棧。同樣,fastcall是由被呼叫函式完成棧平衡工作,如下例子:
void fastcalldemo_fastcall(int w, int x, int y, int z);
編譯器可能產生的程式碼如下:
;demo_fastcall(1,2,3,4)
push 4
push 3
mov edx, 2
mov ecx, 1
call demo_fastcall
所以,雖然函式有4個引數,在由被呼叫函式清理引數時,是需要清理最後兩個引數即可。
4. C++呼叫約定
C++類中的非靜態成員函式與標準函式不同,它們需要使用this指標,該指標指向用於呼叫函式的物件。由於呼叫函式的物件的地址必須由呼叫方提供,所以,在呼叫非靜態成員函式時,this指標必須作為引數傳遞給被呼叫函式。C++語言標準並沒有規定具體的傳遞細節,所以,不同的編譯器採用不同的技巧來傳遞this指標。
MicrosoftVisual C/C++提供thiscall呼叫約定,它將this指標傳遞到ECX暫存器中。並且與stdcall一樣,由非靜態成員函式清除引數。GNU g++編譯器將this看成是任何非靜態成員函式的第一個隱含引數,其他方面則與cdecl呼叫約定相同。
5. 其他呼叫約定
相關推薦
[C++反彙編] 呼叫約定 cdecl stdcall fastcall 等
該系列文章是依據本人平時對反彙編的學習,歸納總結,所做的學習筆記。如有錯誤或待改善之處,請留下您寶貴的意見或建議。 呼叫約定是指呼叫方放置函式呼叫所需的引數的具體位置。具體的位置可以是特定的暫存器、
x86 常見呼叫約定(cdecl,fastcall,stdcall) & x86和ARM呼叫約定的棧幀分析 & ARM ATPCS(ARM-THUMB procedure call standard)
#PS:要轉載請註明出處,本人版權所有 #PS:這個只是 《 我自己 》理解,如果和你的 #原則相沖突,請諒解,勿噴 由於某些工作的需要,我需要掌握X86以及ARM的一些呼叫規則,讓自己可以大致看懂ASM程式碼。於是,我總結了一下我需要的東西
從彙編角度檢視C語言函式呼叫約定【非常有用】
轉自:https://blog.csdn.net/Holmofy/article/details/76094986 為了防止出現不必要的程式碼影響組合語言的檢視,所以程式中不使用任何庫函式,以保持彙編程式碼的簡潔。 這裡所使用的彙編是VC的MASM。 預設函式呼叫方式_
《C++反彙編與逆向分析技術揭祕》讀書總結——建構函式與解構函式
建構函式的必要條件: 這個函式的呼叫,是這個物件在作用域內的第一次成員函式呼叫,看this指標即可以區分物件,是哪個物件的this指標就是哪個物件的成員函式。 使用thiscall呼叫方式,使用ecx傳遞this指標; 返回值為this指標。 解構函式的必要條件: 這
C++反彙編除錯
1、使用 OllyDBG開啟的dll檔案,最好找破解pro版本。不然沒有編輯許可權 ,目前OllyDBG並不支援eclipse IDE 64位編輯的 .class檔案型別。 另外使用反編譯的時候實體記憶體必須大於4G,彙編的時候會佔用很多的計算。 對HEX的資料根據需要編輯之後,使用分析指
C++反彙編學習筆記7——陣列和指標以及他們的定址
兩年前寫的,歡迎大家吐槽! 轉載請註明出處。 1. 陣列在函式內 先通過一個簡單的例子來看一下陣列和普通變數在初始化時的不同之處: 這是陣列初始化: 42: int nArry[5] = {1, 2, 3, 4, 5}; 0042B758 mov
C++反彙編學習筆記6——變數在記憶體中的位置和訪問方式
兩年前寫的,歡迎大家吐槽! 轉載請註明出處。 1. 全域性變數和區域性變數的區別 具有初始值的全域性變數在原始碼連結時就被寫入所建立的PE檔案,當該檔案被執行時作業系統分析各個節中的資料填入對應的記憶體地址中,這時全域性變數就已經存在了,等PE檔案的分析和載入工
c&c++反彙編與逆向分析學習筆記(2)--反彙編靜態分析工具IDA
所謂“靜態分析”,是相對於前面提到的“動態分析”而言的。在“動態分析”過程中,偵錯程式載入程式,並以除錯模式執行起來,分析者可以在程式的執行過程中觀察程式的執行流程和計算記過。但是,在實際分析中,很多場合不方便執行目標,比如軟體的某一模組(無法單獨執行)、病
2018/10/03-函式呼叫約定、cdecl、stdcall、fastcall- 《惡意程式碼分析實戰》
cdecl是最常用的約定之一,引數是從右到左按序被壓入棧,當函式完成時由呼叫者清理棧,並且將返回值儲存在EAX中。 stdcall約定是被呼叫函式負責清理棧,其他和cdecl非常類似。 fastcall呼叫約定跨編譯器時變化最多,但是它總體上在所有情況下的工作方式都是相似的。在fastcall
cdecl、stdcall、fastcall、thiscall函式呼叫約定區別 (轉)
在C語言中,假設我們有這樣的一個函式: int function(int a,int b) 呼叫時只要用result = function(1,2)這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函式呼叫需要
cdecl、stdcall、fastcall函式呼叫約定區別
論函式呼叫約定 在C語言中,假設我們有這樣的一個函式: int function(int a,int b) 呼叫時只要用result = function(1,2)這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中
呼叫約定(pascal,fastcall,stdcall,thiscall,cdecl)區別等
tag:彙編,pascal,fastcall,stdcall,thiscall,cdecl,呼叫約定,函式呼叫約定,返回值傳遞方式 摘要:文章講述了幾種主要程式語言中的函式呼叫約定;詳細說明時主要以VC6中的函式呼叫約定為主,闡釋方式主要是以C++程式編譯後得到的彙編程
反彙編C++ OOP程式碼 分析建構函式如何被呼叫 以及簡單的C++物件記憶體模型
在今天進行C++程式碼的思考時,產生一個疑問,就是C++類的建構函式是如何被呼叫的 於是就做了一個簡單的實驗來驗證自己的想法。 //main.cpp #include <stdio.h> class People{ private: int i; i
C++程式碼反彙編後的函式呼叫過程,堆疊暫存器EBP和ESP
棧是從高地址向低地址生長的。 ebp始終指向當前棧幀的棧底部 , 通過ebp+4中儲存著函式的返回地址 。函式返回時將EBP的值推給EIP ,返回到上一個函幀繼續執行。 ret 與call指令 相反 ,call 將EIP壓入堆疊,然後跳到標號處。 ret 8 在函式返回
彙編函式與c函式互相呼叫問題
彙編函式與c函式的互相呼叫問題 從函式定義的角度來看,彙編函式與c函式都是執行流的基本單位,兩者沒有太大的區別。從操作物件來看,彙編函式中的指令都是直接操作暫存器完成的,c函式中的語句是通過操作變數(可能在暫存器中,大部分時間中都在記憶體中例如ram、cache)。 從c函式調用
c/c++彙編及反彙編命令執行語句
上圖來源於中國MOOK大學南京大學袁春風老師課件,從高階語言到底層二進位制程式碼按此流程一步步進行。 我們首先可以在建立一個c檔案,如果虛擬機器與你的Windows還不可以進行檔案共享,可以開啟Terminal(同windows下的cmd。我的是在/usr/share/Terminal這個
nasm 彙編 與c 語言互相呼叫
NASM 與c 互動 Window nasm 彙編 與c 語言互相呼叫 Windows Linux c函式,提供給彙編使用 編譯過程 環境配置 nasm 彙編 與c 語言互相呼叫 nasm 在
C語言彙編-函式呼叫堆疊的過程
本篇來分析函式呼叫的過程:通過下面一個簡單的例子來進入話題:#include<stdio.h>int sum(int a,int b){ int tmp=0; tmp=a+b; return tmp;}int main(){ int a=10; int b=20;
C語言彙編-函式呼叫棧
函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的程式碼,最後被呼叫者向呼叫者返回結果,還有大家比較熟悉的一句話,就是函式呼叫是在棧上發生的,那麼在計算機內部到底是如何實現的呢? 對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為程式碼段,資料段,堆,棧 程式碼段:儲存程式文字,指令
利用反彙編手段解析C語言函式
轉自: https://blog.csdn.net/songjinshi/article/details/8450419 1、問題的提出 函式是 C語言中的重要概念。利用好函式能夠充分利用系統庫的功能寫出模組獨立、易於維護和修改的程式。函式並不是 C 語言獨有的概念,