首頁>Club>
12
回覆列表
  • 1 # 愛達人程式設計達人

    相信大家對函式這個名詞不會太陌生,彙編的世界中的函式是一系列指令的集合,為了完成某個重複使用的特定功能。我們用例子分析彙編眼中的函式是什麼樣子的。

    例1:向暫存器中賦值

    假如我們需要將1存到四個暫存器中,需要編寫下面四行程式碼:

    MOV EAX,1

    MOV ECX,1

    MOV EDX,1

    MOV EBX,1

    我們把以上四條指令看成一個整體,從某個角度來說,這四條指令就是一個函式,為了實現向暫存器中賦值的功能。

    知道了函式是什麼樣子,具體怎麼執行一個函式呢?執行函式有2種方式,執行函式也叫函式呼叫。

    執行方式:

    1、用JMP指令執行函式

    2、用CALL指令執行函式

    【JMP指令執行函式】

    JMP指令是無條件跳轉指令。

    第一步:在DTDebug.exe軟體中開啟飛鴿軟體,如圖2-12-1所示。

    第二步:輸入例1中的函式,如圖2-12-1所示。

    看圖2-12-2中,我們已經把函式寫入到彙編視窗中,當前函式體開始的地址為0x77068E51,當前EIP暫存器儲存的資料為0x77068E34,我們知道EIP暫存器是儲存下一行將要執行指令的地址,我們怎麼能讓它直接執行到函式地址0x77068E51呢,那就是第三步要做的事情了。

    第三步:JMP指令是無條件跳轉指令,那麼我們只需要用JMP指令跳到函式體開始的記憶體地址就可以了,輸入JMP 0x77068E51,如圖2-12-3所示。

    第四步:按F8執行並觀察資料變化,如圖2-12-4所示。

    看圖2-12-4中所示,黑色定位游標顯示在0x77068E51,EIP暫存器儲存的資料由0x77068E34變為了0x77068E51說明已經跳轉成功。

    第五步:按F8執行並觀察資料變化,看是否實現了函式想要做的事情,如圖2-12-5所示。

    圖2-15-5中,已經實現了把0x00000001存入了相對應的暫存器中。

    以上是JMP指令呼叫函式的步驟,對JMP指令呼叫函式的總結:我們呼叫函式時只要JMP函式體的地址,然後跳轉到函式體開始的位置就可以了。

    【CALL指令執行函式】

    第一步:在DTDebug.exe軟體中開啟飛鴿軟體,如圖2-12-6所示

    第二步:輸入例1中的函式,如圖2-12-7所示。

    看圖2-12-7中,彙編視窗中的黑色定位游標定位在0x77068E34,函式體開始的地址為0x77068E51,EIP儲存的資料為0x77068E34。

    第三步:用CALL指令呼叫函式,輸入CALL 0x77068E51,當前彙編視窗中的黑色定位在0x77068E34,如圖2-12-8所示。

    第四步:按F7執行並觀察資料變化。

    看圖2-12-9中所示,按F7執行後,彙編視窗中的黑色定位游標定位在了0x77068E51,也是函式體開始的記憶體地址,EIP儲存的資料為0x77068E51,說明已經成功跳到函式體開始的記憶體地址,並將自己下一行指令的記憶體地址壓入堆疊中。

    第五步:按F8執行完函式,如圖2-12-10所示。

    看圖2-12-10中,已經實現了把0x00000001存入了相對應的暫存器中。

    如果我想接著從CALL指令的下一行開始執行怎麼辦哪?

    可以用RETN指令,在執行CALL指令時已經將CALL指令下一行指令地址壓入到了堆疊中。

    第一步:輸入RETN指令,如圖2-12-11。

    第二步:按F8執行並觀察資料變化,如圖2-12-12所示。

    看圖2-12-12,執行完RETN指令,可以總結CALL指令一共做了兩件事:第一件事是修改EIP的值,第二件事是把它下一行的地址壓入堆疊中。CALL始終把它下一行地址壓入堆疊,這是一個相對值,保證了函式的可複用性。可以這樣說,CALL就是為函式呼叫所生的。

    為了更好的讓大家理解函式體現是實現具體的功能,我們來編寫一個例子

    例:編寫一個函式,實現任意兩個整數相加。

    實現:

    第一步:在DTDebug.exe軟體中開啟飛鴿軟體,如圖2-12-13所示。

    第二步:因為要實現任意2個整數相加,這裡需要用到2個暫存器,假設這兩個整數分別為1,2,這裡1和2分別代表引數,則輸入

    MOV ECX 1

    MOV EDX 2

    這裡是函式體,實現任意2個整數相加的函式

    ADD ECX,EDX (ECX儲存的資料+EDX儲存的資料的值儲存到ECX中)

    MOV EAX,ECX (所得到的結果放到EAX中)

    如圖2-12-14所示

    第三步:輸入CALL指令,CALL 0x77068E51,如圖2-12-15所示。

    第四步:輸入RETN指令,如圖2-12-16。

    以上是實現任意兩個數的彙編函式編寫,接下來我們驗證是否能實現該功能。

    驗證:

    第一步:按兩次F8,將黑色游標定位到CALL指令那一行,當前ESP儲存的資料為0x0019FFF0如圖2-12-17所示。

    看圖2-12-17中,按兩次F8執行完

    MOV ECX,1

    MOV EDX,2

    把1移動到ECX暫存器的過程我們叫傳遞引數,把2移動到EDX暫存器的過程我們也是傳遞引數,

    第二步:按F7執行,並觀察資料變化,如圖2-12-18所示。

    圖2-12-18彙編視窗中,黑色定位游標顯示在了0x77068E51,該記憶體地址也是函式體開始的地方,且將自己下一行地址壓入到堆疊中,ESP儲存的資料由0x0019FFF0變為了0x0019FFEC,棧頂為0x0019FFEC儲存的資料正是CALL指令下一行地址0x77068E43。

    第三步:按F8兩次執行到RETN指令處並觀察資料變化,如圖2-12-19。

    圖2-12-19彙編視窗中,黑色定位游標已經顯示在了RETN指令那一行,暫存器視窗中,EAX也儲存了ECX儲存的資料加上EDX儲存的資料,執行到這裡,說明我們編寫的程式正確實現了兩個數相加的功能。

    第四步:按F8執行並觀察資料變化,如圖2-12-20所示。

    看圖2-12-20彙編視窗中,F8執行完後,返回到了CALL指令下一行。

    總結:在實現任意兩個數相加函式時遇到了幾個知識點,第一個是引數,第二個是呼叫函式,第三個是引數傳遞,第四個是返回值。

    引數:看實現第二步中,

    MOV ECX,1

    MOV EDX,2

    1和2就是例題的引數。

    引數傳遞:看驗證第一步中,執行完。

    MOV ECX,1

    MOV EDX,2

    把1儲存到ECX的過程叫引數傳遞,把2儲存到EDX的過程也是引數傳遞。

    呼叫函式:看驗證第一步、第二步中,先將黑色定位游標定位到CALL指令那一行,CALL呼叫函式體開始的記憶體地址0x77068E51,按F7執行完後,執行到函式體開始的地方,整個過程叫函式呼叫。

    返回值: 看驗證第二步、第三步中,執行完。

    ADD ECX,EDX

    MOV EAX,ECX

    把EDX儲存的資料與ECX儲存的資料相加的結果儲存到ECX中,接著把ECX中儲存的結果移動到EAX中。一般情況下EAX用來儲存返回值的。

    思考:如果要傳遞的引數比較多,比如10個,該如何實現呢?

    ---摘自本人拙著:程式設計達人內部教材《彙編、C語言基礎教程》

  • 中秋節和大豐收的關聯?
  • 黑洞引力強到連光都逃不出來,那還看的到它嗎?