500 likes | 743 Views
第 8 章 : 進階程序. 章節概要. 區域變數 堆疊參數 堆疊框 遞迴 建立多模組程式. 區域指令. 一個區域變數在一個程序裡面被產生使用,而且破壞 區域指令宣布一連串的區域變數 立刻遵循 PROC 指令 每個變數被分配一個類型 語法 : LOCAL varlist 例題 :. MySub PROC LOCAL var1:BYTE, var2:WORD, var3:SDWORD. 區域變數. 例題 :. LOCAL flagVals[20]:BYTE ; array of bytes
E N D
章節概要 • 區域變數 • 堆疊參數 • 堆疊框 • 遞迴 • 建立多模組程式
區域指令 • 一個區域變數在一個程序裡面被產生使用,而且破壞 • 區域指令宣布一連串的區域變數 • 立刻遵循 PROC 指令 • 每個變數被分配一個類型 • 語法: LOCAL varlist 例題: MySub PROC LOCAL var1:BYTE, var2:WORD, var3:SDWORD
區域變數 例題: LOCAL flagVals[20]:BYTE ; array of bytes LOCAL pArray:PTR WORD ; pointer to an array myProc PROC, ; procedure p1:PTR WORD ; parameter LOCAL t1:BYTE, ; local variables t2:WORD, t3:DWORD, t4:PTR DWORD
MASM-生成代碼 (1 of 2) BubbleSort PROC LOCAL temp:DWORD, SwapFlag:BYTE . . . ret BubbleSort ENDP MASM產生下列的編碼: BubbleSort PROC push ebp mov ebp,esp add esp,0FFFFFFF8h ; add -8 to ESP . . . mov esp,ebp pop ebp ret BubbleSort ENDP
MASM-生成代碼 (2 of 2) 堆疊的圖表為 BubbleSort 程序構成:
堆疊參數 • 暫存器和堆疊參數比較 • INVOKE 指引 • PROC 指引 • PROTO 指引 • 通過按價值計算比路過參考 • 參數分類 • 例子:交換二個整數 • 問題解析訣竅
暫存器和堆疊參數比較 • 暫存器參數需要把一個暫存器獻給每個參數。 • 想像呼叫 DumpMem 程序的二個可能方法。清楚地其次比較容易: pushad mov esi,OFFSET array mov ecx,LENGTHOF array mov ebx,TYPE array call DumpMem popad push OFFSET array push LENGTHOF array push TYPE array call DumpMem
INVOKE 指引 • 那喚起指令是英代爾的呼叫指令的一個有力的替換讓你通過多個爭論 • 語法: INVOKE程序名稱 [引數清單 ] • ArgumentList是一個傳遞給程序的可選用引數清單 • Arguments 能是: • 立即的值和整數語法 • 變數名字 • 位址和 ADDR 語法 • 暫存器名字
INVOKE 例題 .data byteVal BYTE 10 wordVal WORD 1000h .code ; direct operands: INVOKE Sub1,byteVal,wordVal ; address of variable: INVOKE Sub2,ADDR byteVal ; register name, integer expression: INVOKE Sub3,eax,(10 * 20) ; address expression (indirect operand): INVOKE Sub4,[ebx]
ADDR運算子 • 會根據程式的記憶體模式所要求,來傳遞 near 指標或 far 指標。 • 小的模型:回復16位元補償 • 大的模型:回復32位元分割/補償 • 平坦的模型: 回復32位元補償 • 簡單例題: .data myWord WORD ? .code INVOKE mySub,ADDR myWord
PROC 指引 • PROC 指令用一個命名的參數可選擇目錄宣布一個程序. • 語法: label PROC paramList • paramList 是被逗點分開的一連串的參數。每個參數有下列的語法: paramName:type 樣式必須或是標準ASM類型 (BYTE, SBYTE ,WORD,etc)之一, 否則它可能是對這些類型之一的一個指標.
PROC 例題 (1 of 3) • AddTwo 程序在 EAX 接受二個整數而且回復他們的總數. • C++ 程式典型地在EAX回復來自功能的32位元整數。 AddTwo PROC, val1:DWORD, val2:DWORD mov eax,val1 add eax,val2 ret AddTwo ENDP
PROC 例題 (2 of 3) FillArray 接受對位元組的排列一個指標,一個位元組填滿將到排列的每種元素 , 和排列的大小被複制的值。 FillArray PROC, pArray:PTR BYTE, fillVal:BYTE arraySize:DWORD mov ecx,arraySize mov esi,pArray mov al,fillVal L1: mov [esi],al inc esi loop L1 ret FillArray ENDP
PROC 例題 (3 of 3) • Swap PROC, • pValX:PTR DWORD, • pValY:PTR DWORD • . . . • Swap ENDP ReadFile PROC, pBuffer:PTR BYTE LOCAL fileHandle:DWORD . . . ReadFile ENDP
PROTO 指引 • 產生一個程序原型 • 語法: • label PROTO paramList • 每個程序順道拜訪那喚起指令一定要有一個原型 • 一種完全的程序定義也能視為ownprototype
PROTO 指引 • 標準結構: PROTO 在程式項目表的頂端出現,在編碼分割中INVOKE出現,和程序履行稍後在程式中發生 MySub PROTO ; procedure prototype .code INVOKE MySub ; procedure call MySub PROC ; procedure implementation . . MySub ENDP
PROTO 例題 • 為 ArraySum 程序原型,顯示它的參數目錄: ArraySum PROTO, ptrArray:PTR DWORD, ; points to the array szArray:DWORD ; array size
MASM 產生下列的編碼: push myData call Sub1 經過價值 • 當一個程序引數被經過價值的時候,複製16位元或32位元整數是推動堆疊。 • 例題: .data myData WORD 1000h .code main PROC INVOKE Sub1, myData
MASM 產生下列的編碼: push OFFSET myData call Sub1 參考經過 • 當一個引數被經過參考的時候,它的位址是推動堆疊. 例題: .data myData WORD 1000h .code main PROC INVOKE Sub1, ADDR myData
參數分類 • 輸入: 輸入參數即為,由呼叫者程式傳遞給被呼叫程序的資料。我們不期望被呼叫程序修改 到對應的參數變數,即使它這麼做了,修改的效果也應該只侷限在程序本身以內。 • 輸出: 當呼叫者程式傳遞某個變數的位址給一個程序時,就會建立起輸出參數。 程序可以使 用這個位址找到該變數,並且指定資料給該變數。 • 輸入 - 輸出: 輸入 - 輸出參數與輸出參數幾乎完全相同,它們的差異點是: 被呼叫的程序會預 期,由參數所參照的變數將含有一些資料。
範例: 交換兩個整數 使用到的 Swap 程序具有兩個 輸入 - 輸出參數,其名稱分別為 pValX 和 pValY,這兩個參數所含有的,就是要進行交換 的資料的位址: • Swap PROC USES eax esi edi, • pValX:PTR DWORD, ; pointer to first integer • pValY:PTR DWORD ; pointer to second integer • mov esi,pValX ; get pointers • mov edi,pValY • mov eax,[esi] ; get first integer • xchg eax,[edi] ; exchange with second • mov [esi],eax ; replace first integer • ret • Swap ENDP
除錯的訣竅 • 當他們被一個程序修正的時候,儲存而且回復暫存器. • 除了一個暫存器回復產生一個函數 • 當使用INVOKE的時候,小心通過一個指標給正確的資料類型. • 舉例來說, MASM 不能夠區別一個 DWORD 引數和一個 PTR 位元組引數。 • 不要傳遞給它立即值引數. • 遵從它的位址將或許引起一種普通保護的過失。
堆疊框 • 記憶體模式 • 語言 Specifiers • 明確准入堆疊參數 • 經過引數的參考 • 創造區域變數
堆疊框 • 知道當做一筆啟動記錄 • 堆疊的區域對於一個程序的回復位址留存,經過參數,保存了暫存器,和區域變數 • 藉著下列步驟產生: • 呼叫子常式,因而導致子常式的返回位址被壓進堆疊中. • EBP 設定等於 ESP。 從這個時候開始,EBP 的作用是當作所有子常式參數的基底參考。. • 如有任何區域變數存在,則我們可將 ESP 減去某值,以便在堆疊中保留區域變數的儲存空間。
記憶體模式 • 一個程式的記憶體模式決定數字和編碼和資料分割的大小. • 實體位址模式下的程式tiny, small, medium, compact, large,和huge模式. • 在保護模式下的程式,使用的是 flat 記憶體. Small model: code < 64 KB, data (including stack) < 64 KB. All offsets are 16 bits. Flat model: single segment for code and data, up to 4 GB. All offsets are 32 bits.
.MODEL 指令 • 模式的指定符一個程式的記憶體模式和模式的選擇。(語言-指定符) • 語法: .MODEL memorymodel [,modeloptions] • 記憶體模式可能是下列各項之一: • tiny, small, medium, compact, large, huge, 或 flat • 模是選項包括語言指定符: • 程序取名方案 • 參數經過慣例
語言指定符 • C: • 指定符會要求程序引數,以相反順序壓進堆疊(向右離開) • 呼叫程式清除堆疊 • pascal • 程序引數順向壓進(由左至右) • 呼叫程式清除堆疊 • stdcall • 指定符會要求程序引數,以相反順序壓進堆疊(由右至左) • 呼叫程式清除堆疊
明確經過堆積參數 • 一個程序能明確取得使用來自 EBP1 的常數彌補的堆疊參數. • 例題: [ebp + 8] • 因為它支撐堆疊結構的基礎位址,所以 EBP 時常被稱為基礎的指標或者結構指標. • EBP 不在程序的時候改變值. • 當一個程序回復時,EBP一定要對它的最初值回復.
堆疊框例子(1 of 2) .data sum DWORD ? .code push 6 ; second argument push 5 ; first argument call AddTwo ; EAX = sum mov sum,eax ; save the sum AddTwo PROC push ebp mov ebp,esp . .
堆疊框例題 (2 of 2) • AddTwo PROC • push ebp • mov ebp,esp ; base of stack frame • mov eax,[ebp + 12] ; second argument (6) • add eax,[ebp + 8] ; first argument (5) • pop ebp • ret 8 ; clean up the stack • AddTwo ENDP ; EAX contains the sum
習題. . . • 產生一個叫做減去來自第二個的第一個引數不同的程序。下列各項是一個樣本呼叫: push 14 ; first argument push 30 ; second argument call Difference ; EAX = 16 • Difference PROC • push ebp • mov ebp,esp • mov eax,[ebp + 8] ; second argument • sub eax,[ebp + 12] ; first argument • pop ebp • ret 8 • Difference ENDP
參考經過引數(1 of 2) • ArrayFill 程序,可使一個陣列填入由 16 位元整數所 組成的擬隨機數列。 • 第一 個引數利用參考的方式加以傳遞,第二個則使用傳值的方式傳遞。 以下是一個示範呼叫: .data count = 100 array WORD count DUP(?) .code push OFFSET array push COUNT call ArrayFill
參考經過引數(2 of 2) ArrayFill 能參考排列不知道排列的命名: ArrayFill PROC push ebp mov ebp,esp pushad mov esi,[ebp+12] mov ecx,[ebp+8] . . ESI 指向排列的開始,因此,使用一個迴路取得每種排列元素是容易的.顯示完全的計畫.
LEA指令 • LEA 指令用於回傳間接運算元的位移. • 彌補運算能唯一的回復常數彌補. • 當獲得堆疊參數或區域變數的彌補時, 需要LEA .舉例來說: CopyString PROC, count:DWORD LOCAL temp[20]:BYTE mov edi,OFFSET count ; invalid operand mov esi,OFFSET temp ; invalid operand lea edi,count ; ok lea esi,temp ; ok
產生區域變數 • 為了要明確地產生區域變數,減去來自 ESP 總數大小. • 下列的例子產生並且設定二個 32 位元區域變數初值: (我們稱它們為locA和locB) MySub PROC push ebp mov ebp,esp sub esp,8 mov [ebp-4],123456h ; locA mov [ebp-8],0 ; locB . .
遞迴 • 遞迴是甚麼? • 遞迴計算總數 • 計算階乘
遞迴是甚麼? • 程序產生當. . . • 一個程序呼叫本身 • 程序A呼叫程序B,依次呼叫程序A • 使用一個圖表在每個節是程序和每個邊緣是一個程序呼叫,遞迴形成一個周期:
Stack frame: 遞迴計算總數 CalcSum 程序遞迴計算整數排列的總數: ECX = count. Returns: EAX = sum • CalcSum PROC • cmp ecx,0 ; check counter value • jz L2 ; quit if zero • add eax,ecx ; otherwise, add to sum • dec ecx ; decrement counter • call CalcSum ; recursive call • L2: ret • CalcSum ENDP 顯示完全計畫
計算階乘(1 of 3) 這一個功能計算整數 n 的階乘。n 的新數值在每個堆疊框中被保存: int function factorial(int n) { if(n == 0) return 1; else return n * factorial(n-1); } As each call instance returns, the product it returns is multiplied by the previous value of n.
計算階乘(2 of 3) Factorial PROC push ebp mov ebp,esp mov eax,[ebp+8] ; get n cmp eax,0 ; n < 0? ja L1 ; yes: continue mov eax,1 ; no: return 1 jmp L2 L1: dec eax push eax ; Factorial(n-1) call Factorial ; Instructions from this point on execute when each ; recursive call returns. ReturnFact: mov ebx,[ebp+8] ; get n mul ebx ; ax = ax * bx L2: pop ebp ; return EAX ret 4 ; clean up stack Factorial ENDP 看計畫項目表
計算階乘(3 of 3) 假如我們想要計算 12! 這一個圖表顯示第一個少數堆疊框架 藉著對階乘的遞迴呼叫產生 每個遞迴的呼叫使用 12個位元組的堆疊空間.
多模組程式 • 一個多模組程式是一個原始碼已經進入隔開ASM檔案之內被均分的程式. • 每個ASM檔案 (模組)進入一個隔開的 OBJ 檔案之內被裝配。 • 屬於相同的程式所有的 OBJ 檔案被連接使用連結實效進入一個 EXE 檔案. • 這一個程序叫做靜態連接
優點 • 當區分為隔開的原始碼組件,大的程式比較容易寫,保持,而且除錯. • 當改變一列編碼的時候,它關閉模組需要在再一次被組合。 • 一個模組可能是合乎邏輯相關的編碼和資料的一個容器(物體-在這裡導向…)。 • 封裝 :將模組內的程 序隱藏起來
產生一個多模組程式 • 當產生一個多模組計畫的時候,這裡是遵從的一些基本步驟: • 產生主要的模組 • 為每個程序或相關程序的模組產生一個隔開的原始碼組件 • 產生一包括為外部的程序包含程序原型的檔案(在模組之間被叫做) • 使用那INCLUDE指令使你的程序設計原型可得的到每個模組
範例: ArraySum程式 • ArraySum 程式是在第 5 章出現 四個白色的長方形將變成每一個模組.
例子程式輸出 Enter a signed integer: -25 Enter a signed integer: 36 Enter a signed integer: 42 The sum of the integers is: +53
INCLUDE 檔案 sum.inc 檔案包含原型外部的功能沒有在Irvine 32 資料庫中: INCLUDE Irvine32.inc PromptForIntegers PROTO, ptrPrompt:PTR BYTE, ; prompt string ptrArray:PTR DWORD, ; points to the array arraySize:DWORD ; size of the array ArraySum PROTO, ptrArray:PTR DWORD, ; points to the array count:DWORD ; size of the array DisplaySum PROTO, ptrPrompt:PTR BYTE, ; prompt string theSum:DWORD ; sum of the array
檢查個別的模組 • Main • PromptForIntegers • ArraySum • DisplaySum 訂製一組檔案來集合與連接.