匯編語言中的函數(shù)調用參數(shù)傳遞及全局與局部變量與“基址”
作為剛往底層方向走的一只菜鳥,今天為各位分享一篇名為匯編眼中的函數(shù)調用參數(shù)傳遞以及全局、局部變量與“基址”,好了,廢話不多說,先來看看C語言代碼:
本次的分享主要以畫堆棧圖為主,通過畫圖的方式來看看這段代碼是如何運作的
我們先寫一句匯編代碼,mov eax,eax其實這句代碼并沒有什么用,也就是將eax的值移入eax中,這句代碼對于我們的作用僅作為斷點,我們F5運行下程序并且切換到反編譯界面
右鍵之后點擊Go To Disassemble,也就是進入反編譯界面,我們來看看反編譯的代碼
這就是咱們main函數(shù)反編譯后的結果,那么現(xiàn)在我們記錄一下ebp,esp的值,并且畫出現(xiàn)在的堆棧圖
ESP = 0019FEF4
EBP = 0019FF40
黃色=ebp~esp初始的內存
那好,我們繼續(xù)來看代碼
這里是我們自己寫的匯編代碼,編譯器也沒有改動過可以說是本色出演,這里內存沒有變化,好,那么我們接著來看add函數(shù)這里的調用
可以看到函數(shù)調用的時候首先是將3、1這兩個值推入棧中,但是又有疑問了“add函數(shù)的調用是這樣的add(1,3),為啥首先推入棧中的是3而不是1呢?”之所以這樣是因為棧中是先進后出的,所以參數(shù)進入棧中的順序是從右向左的,當然這里也可以看到函數(shù)中的參數(shù)是壓入棧中然后取出來而不是通過通用寄存器eax,edx,ebx這些來傳送參數(shù)的,其實這也好理解,因為通用寄存器只有八個,像esp,eip,ebp這樣的寄存器還不能隨便改,能用的也只有剩下的幾個,參數(shù)超過剩下的幾個咋辦?那就只能用堆棧了。我們繼續(xù)來看F10單步執(zhí)行一下,看看堆棧的變化
我們再看看EBP和ESP
這里可以看到壓入一個3后棧頂指針減去了4h,至于為啥減去4h呢?是因為一個int類型的數(shù)據(jù)寬度是4Byte=32Bit,能存入的最大數(shù)也就是0xFFFFFFFF,16進制數(shù)又是2進制數(shù)的簡寫形式,一個二進制數(shù)需要4Bit來存儲,所以4位二進制數(shù)最大的值為1111轉換為16進制后剛好為F,這樣也方便了開發(fā)者,存儲空間中我們看到的數(shù)據(jù)都是16進制數(shù)
可能以上講得有些出入,如有錯誤,請幫忙糾正,好了,我們接著來看,接下來push 1進去,我們來看看現(xiàn)在的堆棧圖:
繼續(xù)看下面的代碼:
接著是call,call指令在匯編中多用于函數(shù)調用,call指令做了兩個操作,
1.Push 00401103(下一行代碼地址) 2.Jmp 0040100A(函數(shù)地址)
這里我們F11一下,遇到call指令后按F11進入函數(shù)即可,這樣我們就可以看到函數(shù)體中的指令
這里CALL之后看看堆棧中的變化:
繼續(xù)向下走,F(xiàn)11后跳轉到的結果如下
這里是編譯器決定的,不是所有編譯器call后會進入一個jmp指令中轉,再F11一下
Jmp執(zhí)行之后直接進入了函數(shù)體,這里將通用寄存器ebp的值存入棧中,之所以存入棧中是因為每一個函數(shù)中都要使用ebp來尋址,所以需要將ebp的原始值存入棧中,隨后將esp棧頂指針的值移入ebp中,sub esp,40h是將棧頂指針加到40h這個位置,之所以是減40h是因為??臻g是從高到低的,現(xiàn)在我們單步執(zhí)行一下看看棧中的變化
這里40h移動的位置=40h/4h=10h=16,我們看看現(xiàn)在堆棧的變化
Sub esp,40h這段代碼我們是為函數(shù)開辟一塊棧空間出來供函數(shù)存取值的,也就是我們常說的緩沖區(qū),通常用來存儲函數(shù)中的局部變量,我們接著往下看
接著向棧中推入了ebx,esi,edi,棧頂指針[esp-Ch],然后lea指令將[ebp-40h]的地址放入edi中,給ecx賦值為10h=16,也就是循環(huán)16次又將0xCCCCCCCCh賦給eax,這也被稱為斷點字符,然后使用rep指令將緩沖區(qū)中的值賦值為0xCCCCCCCCh,現(xiàn)在我們再來看看堆棧中的變化
我們看一下單步執(zhí)行后的esp和ebp的結果
好勒,咱們接著往下走
這里可以看到首先是將棧中的[ebp+8]=0019FEE4+8=0019FEEC地址中的值移動到eax中,然后將eax與[ebp+Ch]=0019FEE4+Ch=0019FEF0地址中的值相加,并且將相加后的值存入eax中,??臻g無任何變化,變化的僅僅是eax,咱們單步執(zhí)行看下
好了,我們接著往下看
首先將edi,esi,ebx中的值取出來,這里可以看到,我們推入和取出的順序剛好相反,先進后出的道理,隨后將ebp的值移動到esp中,這里也就改變了棧頂指針,然后pop ebp,最后ret,ret的做的操作:
pop eip(這里取出的是返回地址)
咱們單步執(zhí)行一下
我們接著來看代碼
這里esp+8是為了堆棧平衡,恢復最初的堆棧,我們單步一下
堆棧的變化如下:
這樣就和我們沒進入函數(shù)之前的堆棧一樣了,程序到這里就解釋了函數(shù)調用以及傳參的問題。我們接著往下走
函數(shù)調用又是一個call,call的兩個操作:
1、Push 0040110B(下一行代碼地址)
2、Jmp 00401005(函數(shù)地址)
單步會遇到jmp,我們直接單步進入函數(shù)體
對于這里的堆棧就不畫了,主要講解一下這里的全局變量以及“基址”是啥,這里我們的全局變量z是由變量定義的時候分配指定的內存地址,在每一個函數(shù)中都可以找到,每一個全局變量都有一個唯一的內存地址,有且只有一個,在游戲外掛中經(jīng)常會聽到找“基址”,然而這個“基址”就是全局變量的地址,只要程序被編譯那么就只有這么一個指定的地址,我們這個程序中z的地址[00424a30],打開CE
首先我們運行一下我們寫好的程序
選擇我們剛才運行的程序,點擊加入地址,我們將00424a30這個內存地址加入進去
點擊ok,我們看看它的初始值
這里完全沒有自己輸入值,我們改一下值,看看程序的輸出
點擊OK,再看一下改了之后輸出的值
總結:
1、全局變量是編譯后分配的一個指定內存空間,因為是公共的所以任何程序或者程序中的函數(shù)都可以調用以及修改。
2、局部變量的地址是隨機的,因為每次進入函數(shù)都會隨機分配一段地址給函數(shù),這段分配的地址稱為緩沖區(qū),緩沖區(qū)也是用來存儲局部變量的。
3、“基址”就是全局變量,這是外掛開發(fā)中常用到的一個詞匯。
4、函數(shù)調用使用call,call指令做的兩個操作:
(1)push call指令下一行地址
(2)Jmp 函數(shù)地址(編譯器決定,可能先跳轉到中轉地址,然后跳轉到函數(shù)地址)
5、匯編中的函數(shù)就是指令的集合,唯一不同的是函數(shù)最后都會用ret返回
6、函數(shù)中的參數(shù)傳遞是使用堆棧來傳遞的。
以上所述是小編給大家介紹的匯編語言中的函數(shù)調用參數(shù)傳遞及全局與局部變量與“基址”,希望對大家有所幫助!
相關文章
os_object_release Crash 排查記錄分析
這篇文章主要為大家介紹了os_object_release Crash 排查記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11匯編語言80x86系統(tǒng)通用數(shù)據(jù)傳送指令詳解
這篇文章主要為大家介紹了匯編語言80x86系統(tǒng)通用的數(shù)據(jù)傳送指令詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2021-11-11