相信大家對函式這個名詞不會太陌生,彙編的世界中的函式是一系列指令的集合,為了完成某個重複使用的特定功能。我們用例子分析彙編眼中的函式是什麼樣子的。
例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 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指令下一行。
總結:在實現任意兩個數相加函式時遇到了幾個知識點,第一個是引數,第二個是呼叫函式,第三個是引數傳遞,第四個是返回值。
引數:看實現第二步中,
1和2就是例題的引數。
引數傳遞:看驗證第一步中,執行完。
把1儲存到ECX的過程叫引數傳遞,把2儲存到EDX的過程也是引數傳遞。
呼叫函式:看驗證第一步、第二步中,先將黑色定位游標定位到CALL指令那一行,CALL呼叫函式體開始的記憶體地址0x77068E51,按F7執行完後,執行到函式體開始的地方,整個過程叫函式呼叫。
返回值: 看驗證第二步、第三步中,執行完。
ADD ECX,EDX
MOV EAX,ECX
把EDX儲存的資料與ECX儲存的資料相加的結果儲存到ECX中,接著把ECX中儲存的結果移動到EAX中。一般情況下EAX用來儲存返回值的。
思考:如果要傳遞的引數比較多,比如10個,該如何實現呢?
---摘自本人拙著:程式設計達人內部教材《彙編、C語言基礎教程》
相信大家對函式這個名詞不會太陌生,彙編的世界中的函式是一系列指令的集合,為了完成某個重複使用的特定功能。我們用例子分析彙編眼中的函式是什麼樣子的。
例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語言基礎教程》