從彙編角度分析C語言的過程調用

来源:http://www.cnblogs.com/TingyunAPM/archive/2016/09/20/5888788.html
-Advertisement-
Play Games

➠更多技術乾貨請戳:聽雲博客 基本術語定義 1.系統棧(system stack)是一個記憶體區,位於進程地址空間的末端。 2.在將數據壓棧時,棧是自頂向下增長的,該記憶體區用於函數的局部變數提供記憶體。它也支持在調用函數時傳遞參數。 3.如果調用了嵌套的過程,棧會自上而下增長,並接受新的活動記錄(act ...


更多技術乾貨請戳:聽雲博客

基本術語定義

1.系統棧(system stack)是一個記憶體區,位於進程地址空間的末端。

2.在將數據壓棧時,棧是自頂向下增長的,該記憶體區用於函數的局部變數提供記憶體。它也支持在調用函數時傳遞參數。

3.如果調用了嵌套的過程,棧會自上而下增長,並接受新的活動記錄(activation record)來保存一個過程所需的所有數據。

4.當前執行過程的活動記錄,由標記頂部位置的幀指針(frame point)和標記底部位置的棧指針(stack point)定義。

5.在過程執行時,雖然其頂部的限制是固定的,但底部的限制是可以擴展的(在需要更多記憶體空間時)。

分析棧幀(分析如下)

上圖第2個棧幀的分析如下: 

1、在棧幀頂部是返回地址,以及保存的舊的幀指針。返回地址指定了當前過程結束時代碼的控制流轉向的記憶體地址,而保存的舊的幀指針則是前一個活動記錄的幀指針。在當前過程結束後,該幀指針的值可用於重建調用過程的棧幀,在試圖調試調用棧回溯時,這一點很重要。

2、活動記錄的主要部分是為過程調用局部變數分配的記憶體空間。在C中,這種變數也稱為自動變數(automatic variable)。

3、在函數調用時,以參數形式傳遞到函數的值,存儲在棧的底部。

4、所有常見的電腦體繫結構都提供了以下兩個棧操作指令:

  • push指令將一個值放置在棧上,並將棧指針esp減去該值所占用的記憶體位元組數。棧的末端下移到更低的地址;

  • pop指令從棧中彈出一個值,並相應增加棧指針esp的值,也就是說,棧的末端上移了。

5、一般體繫結構另外提供兩個指令,用於調用和退出函數(自動返回到調用過程),它們也會自動操作棧:

  • call指令將指令指針的當前值壓棧,跳轉到被調用函數的起始地址。  call 指令 :在AT&T彙編中,call foo(foo是一個標號)等效於以下彙編指令: pushl %eip ,movl f, %eip  ;   

  • return指令從棧上彈出返回地址,並跳轉到該地址。過程的實現必須將rerurn作為最後一條指令,由call放置在棧上的返回地址位於棧的底部(實際上是上一個活動記錄的底部,當前活動記錄的頂部)。  ret指令: 在AT&T彙編中,ret等效於以下彙編指令: popl %eip

過程調用兩個組成步驟

1、在棧中建立參數列表。傳遞到被調用函數的第一個參數最後入棧(從右到左)。這使得C中可以傳遞可變數目的參數,然後將其從棧上逐一彈出(pop)。

2、調用call,這將指令指針的當前值(call之後的下一條指令)壓棧,代碼的控制流轉向被調用的函數。被調用的過程負責管理幀指針ebp,需要執行下列步驟: 

  • 前一個幀指針壓棧,因而棧指針下移。

  • 將棧指針的當前值copy給幀指針,標記當前執行函數的棧區的起始位置。

  • 執行當前函數的代碼。

  • 在函數結束時,存儲的舊幀指針位於棧的底部。其值從棧彈出到幀指針寄存器(ebp),使之指向前一個函數的棧區起始位置。現在,對當前函數執行call指令時壓棧的返回地址位於棧低。

  • 調用return,將返回地址從棧彈出。cpu轉移到返回地址,代碼的控制流也返回到調用函數。

具體C 語言例子分析

初看起來,這種方法似乎有些混亂,因此,我們先看一個簡單的C語言例子:

在IA-32系統上,彙編代碼本身必須是AT&T表示法給出。 

AT&T彙編語法總結為以下5條規則,就足夠了。 

1.寄存器通過在名稱前加百分號(%)首碼引用。example:為使用eax寄存器,彙編代碼中將使用%eax。(如果在C中內聯彙編的話,C代碼必須指定兩個百分號,才能在轉給彙編器的輸出中形成一個百分號)。

2.源寄存器總是在目的寄存器之前指定。 example,在mov語句中,這意味著 mov a,b 將 寄存器a中的值 內容copy到寄存器b中。

3.操作數的長度由彙編語句的尾碼指定。b代步byte,w代表word,l代表long。在IA-32上,將一個長整型從eax寄存器移動到ebx寄存器中,需要指定movl %eax,%ebx。

4.間接記憶體引用(指針反引用)需要將寄存器包含在括弧中,example:movl(%eax),%ebx 將寄存器eax的值指向的記憶體地址中的長整型copy到ebx寄存器中。

5.offset(register)指定寄存器值與一個偏移量聯用,將偏移量加到寄存器的實際值上。example: 8(%eax)指定將eax+8用作一個操作數。該表示法主要用於記憶體訪問,例如指定與棧指針或幀指針的偏移量,以訪問某些局部變數。

我們來分析一下 main.s 彙編代碼: 

1.從main 主函數開始分析. 在IA-32系統中,ebp寄存器用於幀指針(棧頂),pushl  %ebp 將該ebp寄存器中的值壓入系統棧上最低位置,這導致棧頂指針向下移動4byte,這是因為IA-32系統上需要4byte來表示一個指針(pushl中的尾碼l,在AT&T彙編中表示一個long型)。

2.第3行,movl   %esp, %ebp  將esp(棧指針)寄存器 的值 copy到ebp(幀指針)寄存器中;把當前的棧指針作為本函數的幀指針。

3.第4行,subl $24,%esp 從棧指針減去0x18 byte,使得棧指針下移,將棧的空間增大了0x18=24byte;

  • 調整棧指針,為局部變數保留空間。局部變數必須放置在棧上,在C代碼中,a與b兩個局部變數,兩者都是整型變數,在記憶體中都需要4個byte。

  • 因為棧的前4個byte保存了 幀指針的舊值(上一個活動記錄),編譯器將接下來的兩個 4byte記憶體分配給了這兩個局部變數。

  • ebp - 0xC 存著局部變數a的值 3    ; ebp - 0x8 存著局部變數b的值 4 (這裡可以看到參數是從右到左 壓入棧的)。

4.第5行 ,第6行 movl $0x3, -0xC(%ebp)    movl $0x4, -0x8(%ebp) : 為了向分配的記憶體空間設置初始值(對應C中 局部變數的初始化),編譯器使用了處理器的指針反引用選項。 這兩天指令通知編譯器,引用“幀指針減12”得到的值 在記憶體中指向的位置。使用mov指令將值3 寫入該位置。

  • 編譯器接下來用同樣的方法處理第2個局部變數,其在棧的位置稍低,ebp - 0x8 (ebp - 8byte) 位置 ,值為4。

5.第7行,第8行設置第2個參數(b),第9行,第10行負責設置第1個參數(a)。     movl    -8(%ebp), %eax   ;   movl    %eax, 4(%esp)  ; movl    -12(%ebp), %eax;    movl    %eax, (%esp)

  • 局部變數a和b必須用作即將調用的add過程調用的參數。編譯器通過將適當的值放置在棧的末端來建立參數列表。

  • 如前所述,第一個參數在最低部。棧指針用於查找棧的末尾。

  • 記憶體中對應的位置通過指針反引用確定。將棧上的兩個局部變數的值分別讀入eax寄存器,然後將eax的值寫入參數列表中對應的位置。(一般情況)

6.上圖描述了 add()函數調用前後,棧的狀態。現在可以使用call 指令調用add()函數。call指令 將eip(指令指針寄存器)壓入棧,代碼控制流在add常式的開始處恢復執行。

  • 根據調用約定,常式首先將此前的幀指針(ebp)壓入棧,並將棧指針(esp)賦值給 幀指針(ebp)。

  • 過程的參數可以根據幀指針(ebp)查找。編譯器知道參數就在調用函數的活動記錄末尾,而在當前活動記錄開始處又存儲了兩個4byte的值(返回地址,舊幀指針)。因此參數可以通過反引用ebp+8和ebp+12訪問。

  • add 指令用於 加法,而eax寄存器用作工作空間。結果值就保存在該寄存器中,使它可以傳遞給調用函數(這裡是main())。

  • 為了返回到調用函數,需要執行以下兩個操作: <a>使用pop將存儲的幀指針(ebp)從棧彈出到ebp寄存器。棧幀的頂端重新恢復到main()的設置;<b>ret將返回地址從棧彈出到 eip(指令指針)寄存器,控制流轉向該地址。

7.因為main()中還使用了另一個局部變數(ret)來存儲add()函數的返回值,返回後需要將eax寄存器的值 copy 到ret在棧上的位置。

總結

關於AT&T彙編

enter指令
在AT&T彙編中,enter等效於以下彙編指令:
pushl %ebp # 將%ebp壓棧
movl %esp %ebp # 將%esp保存到%ebp, 這兩步是函數的標準開頭
leave指令
在AT&T彙編中,leave等效於以下彙編指令:
movl %ebp, %esp
popl %ebp
call指令
在AT&T彙編中,call foo(foo是一個標號)等效於以下彙編指令:
pushl %eip
movl f, %eip
ret指令
在AT&T彙編中,ret等效於以下彙編指令:
popl %eip

(個人理解)彙編可以用一句話概括:彙編就是在(寄存器和寄存器)或 (寄存器和記憶體)之間來回move 數據;就是指:數據在記憶體和寄存器間來迴流動,流動的越頻繁就代表程式越複雜,比如office這樣的大型軟體。

從C語言層面分析: 

EBP-xx     一般 是局部變數

EBP+xx   一般都是參數 

EBP+4  返回地址 ,制高點,   很多攻擊都是攻擊這裡, 殺毒軟體,這裡是重點會掃描。

C函數堆棧中分配的空間,並不會清零,所以在寫C代碼的時候,局部變數一定要初始化賦值。

參數的傳遞形式、傳遞順序已經棧平衡並不是固定的(不同的函數調用約定)。

關於 寄存器 與記憶體的區別:   

寄存器位於cpu內部,執行速度快,但比較貴。

記憶體速度相對較慢,成本低,所以容量能做很大。

寄存器和記憶體沒有本質區別,都是用於存儲數據的容器,都是定寬的。

寄存器常用的8個通用寄存器 :EAX,ECX,EDX,EBX,   ESP, EBP, ESI, EDI.

電腦中的幾個常用計量單位:BYTE, WORD, DWORD :BYTE(位元組) = 8bit ; WORD (字 ) = 16bit ; DWORD (雙字)=32bit; 

記憶體的數量特別龐大,無法每個記憶體單元都命名一個名字,所以用編號來替代。

我們稱電腦CPU是32bit或者64bit,有很多書上說之所以叫32bit電腦是因為寄存器的寬度是32bit,這是不准確的,因為還有很多寄存器是大於32bit的。

原文鏈接:http://blog.tingyun.com/web/article/detail/1132


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、B/S和C/S 1、C/S C/S 架構是一種典型的兩層架構,其全程是Client/Server,即客戶端伺服器端架構,其客戶端包含一個或多個在用戶的電腦上運行的程式,而伺服器端有兩種,一種是資料庫伺服器端,客戶端通過資料庫連接訪問伺服器端的數據;另一種是Socket伺服器端,伺服器端的程式通過 ...
  • using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Syste ...
  • 想法:將屏幕截圖作為程式背景圖,在之上彈出提示視窗,選擇確定後進行定時圖片隨機位置顯示。(支持ESC鍵退出) 需要添加的控制項:Timer 需要修改的Form1屬性為下圖紅色區域: 資源文件的添加:添加->新建項->資源文件 ESC鍵退出程式: 在Form1.Designer.cs中增加 this.K ...
  • 相同點: 1、都可以被繼承 2、都不能被實例化 3、都可以包含方法聲明 4、派生類必須實現未實現的方法 區別: 1、抽象基類可以定義欄位、屬性、方法實現。介面只能定義屬性、索引器、事件、和方法聲明,不能包含欄位。 2、抽象類是一個不完整的類,需要進一步細化,而介面是一個行為規範。微軟的自定義介面總是 ...
  • 1.出現invalid signature 如果是微信web開發著工具出現的話,那麼可以用手機試試看,是不是有問題。 2.出現支付一閃而過 一般是授權問題,需要在微信支付-開發配置-測試授權目錄裡面添加授權目錄(註意是區分大小寫的,精確到Controller就行了,也就是二級目錄) 2.Redire ...
  • 使用jQuery調用WebApi有時會遇到跨域的問題,今天介紹一種可以簡單解決跨域問題的方法。 當我們跨域請求WebAPI的時候會提示以下信息: XMLHttpRequest cannot load http://localhost:9641/api/news/GetData. No 'Access ...
  • 0 Asp.Net Core 項目實戰之許可權管理系統(0) 無中生有 1 Asp.Net Core 項目實戰之許可權管理系統(1) 使用AdminLTE搭建前端 2 Asp.Net Core 項目實戰之許可權管理系統(2) 功能及實體設計 3 Asp.Net Core 項目實戰之許可權管理系統(3) 通過 ...
  • 一、Paramiko模塊 1.Paramiko安裝 Python的目錄下有個Scripts目錄,cd到這個目錄用這裡面的pip命令(如果添加的環境變數可以在cmd直接輸入命令):pip install paramiko。如果pip版本低會有提示,python -m pip install --upg ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...