《Intel+組合語言程式設計(第五版》——第8章 進階過程

來源:互聯網
上載者:User

STDCALL 看到 EBP+幾  就得 RET 4*N  除非 偽指令定義好的  PROC USES 參數,參數

C            在 調用者 CALL ADDTEWO    後 ADD ESP,8 

 

1 為什麼返回RET 有時候會出錯誤:STDCALL約定 子程式 ADDTWO 要清理參數

   RET 8的含義(MOV ESP,EBP   RET    之後 ADD ESP,8清除 參數)

   C約定 是調用者 直接ADD ESP,8  直接清理然後找到了返回地址

      C 約定 先(CALL已經返回)清理 在返回 別的調用者
         STDCALL  先返回返回調用者後清理 

-------------------------------------------------------------------------------

  局順-   反+參

關鍵在於參數賦值(程序呼叫前 PUSH 和MOV)和參數的訪問 

   參數賦值 PUSH  5,push 6 這樣的情況 要注意RET  8

  參數訪問 mov eax,[ebp+12]  mov eax,[ebp+8]  ;此時能正常返回 不用看RET

參數的順序:

反向  有4個參數的話 1 [EBP+20] 2 [EBP+16] 3 [EBP+12] 4 [EBP+8]

   C++(4,3,2,1)

-------------------

 2 調用子過程裡有局部變數  push ebp    mov ebp,esp   sub esp,8  ;倆變數 此時 ESP 有變動

   結束之前要銷毀局部變數           mov esp,ebp       ret 能正確返回

順序使用局部變數 第1個 EBP-4      第2個 EBP-8        第3個EBP-12   ESP在最後的局部變數位置上   所以要MOV ESP,EBP

 --------------------------------------------------------------------------------------------------------------------------------

3:種過程形參數()

STDCALL:  RET 4*N

C   :            ADD ESP,4*N 

完整PROC 帶參數 就不考慮 RET +幾

 

 

 

4 局部變數 :
(1)  用LEA返回運行時變數地址 方便雙字對齊

(2)ENTER 和LEAVE為局部變數服務

(3)LOCAL聲明多變數 (自動開闢變數空間,自動leave)

 

 

一 .過程參數:2種基本類型的 寄存器參數(MOV)和堆棧參數(PUSH)。

Irvine32和Irine16庫使用寄存器參數(用的MOV 不是PUSH);缺點代碼混亂,堆棧的參數必須由調用過程壓棧。

(1)寄存器參數 速度快 亂

 pushad<br />mov esi,OFFSET array ;起始位移地址<br />mov ecx,LENGTHOF array ;數目<br />mov ebx,TYPE array ;雙字格式<br />call DumpMem ;顯示記憶體內容<br />popad</p><p>

 

 (2)堆棧參數  靈活(參數順序相反)

push TYPE array<br />push LENGEHOF array<br />push OFFSET array<br />call DumpMem

 

 

 堆棧兩類參數: 值參數(變和常值),引用參數(變數地址)##傳遞數組也是地址##

(1)值傳遞 (變數和常量)

彙編:<br />.data<br />val1 DWORD 5<br />val2 DWORD 6<br />.code<br />push val2<br />push val1<br />call AddTwo</p><p>C++ :<br />int sum =AddTwo(val1,val2) //順序相反</p><p>

 

CALL前 堆棧

 

 

 

 

(2)傳遞引用

彙編:<br />push OFFSET val2<br />push OFFSET val1<br />call Swap</p><p>C++:<br />Swap(&val1,&val2);

調用Swap前 堆棧

 

 

 

 傳遞數組:

.data<br />array DWORD 50 DUP(?) ;未初始化數組<br />.code<br />push OFFSET array<br />call ArrayFill<br />

 

二.堆棧參數的訪問(C++)

1準備:

 訪問子過程參數 要準備2個步驟:

    AddTwo PROC

            push ebp          ; 儲存EBP

 

         

            mov ebp,esp     ;用EBP代替ESP 訪問參數 ;ESP好做別的事情

 

 

2:訪問:

     

  

 AddTwo PROC<br /> push ebp<br /> mov ebp,esp ;堆棧架構的基址</p><p> mov eax,[ebp+12] ;第2個參數<br /> add eax,[ebjp+8] ;第1個參數<br /> pop ebp ;esp 指向返回地址<br /> ret ;pop eip ;esp+4 正常返回<br /> ;因為ESP沒有改變 如果ESP變了就要<br /> ; MOV ESP,EBP</p><p>AddTwo PROC<br />

3:堆棧的清理:

 

 

 

 

         

3種解決:

      (1)add esp,8

               ret

     

      (2)   如果有 push    ebp

                  mov     ebp,esp

         

            mov  esp,ebp

              ret

       (3)   STDCALL 呼叫慣例 : 

                   pop ebp

                   ret     8              ;1個參數是 ret  4   ,2個是8  3個是 12  ,。。。。

 

 

 1) (PUSH的原因)MOVZX擴充8位和16位參數:   如果參數 是8位或者16位的 就不能 PUSH  ‘X’    push  word1 (字)

要用MOVZX變數來和1個寄存器間接 擴充

  .data

           charVal BYTE 'x'

  .code 

            push charVal          ;錯誤

            call   Uppercase

 

改:

      movzx  eax,charVal

      push eax

      call  Uppercase

Uppercase PROC<br /> push ebp<br /> mov ebp,esp</p><p> mov al,[esp+8] ;al =字元<br /> cmp al,'a' ;小於'a'<br /> jb L1 ;是:跳出<br />cmp al,'z' ; 大於'z'<br /> ja L1 ;是 :跳出<br /> sub al,32 ;否 : 轉換大寫<br />L1: pop ebp ;清理堆棧<br /> ret 4<br />Uppercase ENDP

-----------------------------------------------------------------------------------------------------

2)16位

.data

word1 WORD 1234h

word2 WORD 4111h

.code   

          push  word1

push word2

  call AddTwo           ;錯誤

改:

     movzx      eax,word1

      push        eax

      movzx      eax,word2

       push       eax

call AddTwo

  --------------------------------------------------------------------------------------------------

 3)長整數64位:

  .data

logval  DQ  1234567890ABCDEFh

.code

      push DWORD PTR logval+4     ;高雙字 12345678

      push DWORD PTR logval          ;低雙字 90ABCDEF

call WriteHex 64

 

 

 

 

-----------------------------------------------------------------------

 

 ------------------------------------------------------------------------

 三.局部變數

1。局部變數是運行時棧上建立的,在記憶體中起位置在基址指標(EBP)之下,彙編是不給定預設值,運行時候初始化。

 

 

 C++ 局部變數X和局部變數Y:

    void MySub()

{

     int   x=10;

     int   y =20;

 

}

    2個變數8個位元組的空間

 反組譯碼 C約定 :建立,賦值以及堆棧上移除變數

 

 MySub PROC<br /> push ebp<br /> mov ebp,esp<br /> sub esp,8 ;建立局部變數<br /> mov DWORD PTR [ebp-4],10 ;X<br /> mov DWORD PTR [ebp-8],20 ;Y<br /> mov esp,ebp ;在堆棧上刪除局部變數<br /> pop ebp<br />ret<br />MySub ENDP<br />

 

 

 如果沒有 mov esp,ebp   

ESP 在20位置上    書上有錯誤 (是10的位置是錯的)圖上都寫了是ESP 在EBP-8第2個變數

 

 2  lea指令 動態運行時候 返位移地址  (ADDR和OFFSET在保護模式下返回32位位移值),因為Irvine32.inc  中的.MODEL偽指令指定了平坦記憶體模式

(1)   全域變數 擷取變數地址:  mov 寄存器,offset 變數名(老羅的書73頁)

(2)局部變數  EBP操作的  ebp    40100h

                                 變數1    ebp-4=400FCH

     EBP隨著程式的執行環境不同而不同  在編譯的時候就不確定 所以offset擷取不到

                      擷取變數地址:  lea,[ebp-4]

(3)實參賦值:invoke的參數用到一個局部變數地址: (invoke用 addr一起  但是不能和外EAX一起 invoke TEST eax,addr szH);就不能用LEA 和OFFSET 並且 如果是數組的話 指向第2個元素就要+4  INVOKE sWAP,ADDR [R+1],ADDR [R+4]  239頁

   addr  全域變數名 (此時按照OFFSET方法用)

           和局部變數名(自動用LEA指令把地址給EAX,EAX代替變數地址)

但是ADDR 裡不能是[ebp]也不能和MOV一起用

 

 

錯誤:

 

LEA的另一個用法

 

(4)ptr:以不同類型 訪問參數:(老羅71頁)

12

1234 5678h

裝入 記憶體 78 56 34 12

取:         12 34 56 78

1:12h

2:3412h    34

3:7812 3412h

和放到記憶體相反

-----------------------------------------------------

 

 (5)ENTER和LEAVE指令 局部變數的用法

 (6)LOCAL聲明變數(可以與PTR一起表示位元 和[]數組)

後面自動產生leava刪除局部變數空間 不用管RET

注意 位元組變數聲明會分配多餘空間

注意 位元組變數聲明會分配多餘空間

 (6).stack  4096  保留額外的堆棧空間

總結

INVOKE ,ADDR ,PROC和PROTO 指令

 

PROC

 

參數名:類型 也可以是TYPEDEF和STRUCT 變數 和以下的指標

 

有引用參數就不能傳遞立即數;  sub PROC da:ptr word      mov esi,da   INVOKE sub,1000H出現一個保護錯誤記憶體1000H不在資料區段內

 

PROTO

調用 注意前後順序

(1)把PROC改為PROTO

(2)如果有USES偽指令,去掉USES後面的寄存器

(3)類型與實現要匹配

 

 

 

 

建立多個模組OBJ    2種方法:

(1)EXTERN(移植有問題)

(2)INVOKE 和PROTO

 

          1在一個過程 中變成私人 為了是避免變數名與其他模組使用發生衝突

          2多過程   OPTION PROC:PRIVATE 所有過程都成私人

         PUBLEC 匯出  PUBLIC SUB1,SUB2,SUB3

MIAN入口必須是PUBLIC否則找不到入口

 

 

   調用 ;

EXTRN sub@0:PROC

 

call    sub@0   0代表參數  如果2個參數 就是8

跨界使用變數和符號

  變數和符號預設是私人的  要用PUBLIC 匯出指定名字

PUBLIC count ,SYM1

SYM1=10  ;符號

.DATA

count DWORD 0

  

訪問外部:    EXTERN  name :type

    符號: 以EQU和=定義 type就是ABS

    變數: type 是 BYTE,DOWRD     PTR BYTE

ENTERN: one:WORD ,two:SDWORD, three:PTR BYTE, four:ABS

EXTERNDEF代替PUBLIC和EXTERN

 

;vars.inc  一個模組

EXTERNDEF count:DWORD,SYM:ABS

 

1 調用 :在sub.asm 調用 用到INCLUDE vars.inc

 

 

TITILE sub1.asm

.386

.model flat,STDCALL

INCLUDE vars.inc

SYM1=10

.data

count DWORD 0

END

  END 後面省略程式入口標號 也無須聲明 運行時棧

 

 

在MIAN  必須 END main

2

天天     

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.