Windows X64彙編入門

來源:互聯網
上載者:User
最近斷斷續續接觸了些64位彙編的知識,這裡小結一下,一是階段學習的回顧,二是希望對64位彙編新手有所協助。我也是剛接觸這方面知識,文中肯定有錯誤之處,大家多指正。
文章的標題包含了本文的四方面主要內容:
(1)Windows:本文是在windows環境下的組譯工具設計,調試環境為Windows Vista 64位版,調用的均為windows API。
(2)X64:本文討論的是x64彙編,這裡的x64表示AMD64和Intel的EM64T,而不包括IA64。至於三者間的區別,可自行搜尋。
(3)彙編:顧名思義,本文討論的程式設計語言是彙編,其它進階語言的64位編程均不屬於討論範疇。
(4)入門:既是入門,便不會很全。其一,文中有很多知識僅僅點到為止,更深入的學習留待日後努力。其二,便於類似我這樣剛接觸x64彙編的新手入門。
  本文所有代碼的調試環境:Windows Vista x64,Intel Core 2 Duo。

1.  建立開發環境
1.1  編譯器的選擇
    對應於不同的x64彙編工具,開發環境也有所不同。最普遍的要算微軟的MASM,在x64環境中,相應的編譯器已經更名為ml64.exe,隨Visual Studio 2005一起發布。因此,如果你是微軟的忠實fans,直接安裝VS2005既可。運行時,只需開啟相應的64位命令列視窗(圖1),便可以用ml64進行編譯了。第二個推薦的編譯器是GoASM,共包含三個檔案:GoASM編譯器、GoLINK連結器和GoRC資源編譯器,且內建了Include目錄。它的最大好外是小,不用為了學習64位彙編安裝幾個G 的VS。因此,本文的代碼就在GoASM下編譯。

    第三個Yasm,因為不熟,所以不再贅述,感興趣的朋友自行測試吧。
不同的編譯器,文法會有一定差別,這在下面再說。

1.2  IDE的選擇
    搜遍了Internet也沒有找到支援asm64的IDE,甚至連個Editor都沒有。因此,最簡單的方法是自行修改EditPlus的masm文法檔案,這也是我採用的方法,至少可以得到文法高亮。當然,如果你懶得動手,那就用notepad吧。
    沒有IDE,每次編譯時間都要手動輸入不少參數和選項,做個批處理就行了。

1.3  硬體與作業系統
    硬體要求就是64位的CPU。作業系統也必須是64位的,如果在64位的CPU上安裝了32位的作業系統,就算編譯成功也無法運行程式。2.  寄存器的改變
    彙編是直接與寄存器打交道的語言,因此硬體對語言影響很大。先來看看x64與x32相比在硬體上多了什麼,變了什麼(圖2)。
X64多了8個通用寄存器:R8、R9、R10、R11、R12、R13、R14、R15,當然,它們都是64位的。另外還增加了8個128位XMM寄存器,不過通常用不著。
    X32中原有的寄存器在X64中均為擴充為64位,且名稱的第一個字母從E改為R。不過我們還是可以在64位程式中調用32位的寄存器,如RAX(64位)、EAX(低32)、AX(低16位)、AL(低8位)、AH(8到15位),相應的有R8、R8D、R8W和R8B。不過不要在程式中使用如AH之類的寄存器,因為在AMD的CPU上這種用法會與某些指令產生衝突。第一個x64組譯工具
    本節,我們開始編寫自己的第一個x64組譯工具。在這之前,先講一下calling convention的改變。
3.1  API調用方式
    把Calling convention放在第一個講,代表它的重要性。在32位彙編中,我們調用一個API時,採用的是stdcall,它有兩個特點:一是所有參數入棧,通過椎棧傳遞;二是被調用的API負責棧指標(ESP)的恢複,我們在調用MessageBox後不用add esp,14h,因為MessageBox已經恢複過了。
而在x64彙編中,兩方面都發生了變化。一是前四個參數分析通過四個寄存器傳遞:RCX、RDX、R8、R9,如果還有更多的參數,才通過椎棧傳遞。二是調用者負責椎棧空間的分配與回收。
    下面給出一段代碼,功能是顯示一個簡單的MessageBox,注意對RSP的操作:
代碼:程式碼:;範例程式碼1.asm
;文法:GoASM
DATA SECTION
text     db 'Hello x64!', 0
caption  db 'My First x64 Application', 0

CODE SECTION
START:
sub rsp,28h
xor r9d,r9d
lea r8, caption
lea rdx, text
xor rcx,rcx
call MessageBoxA
add rsp,28h
ret這段代碼是在GoASM中編譯,指令部分GoASM與ML64差不多,關鍵是一些宏的定義有差別。比如masm中的.code,在這裡就成了CODE SECTION。下面再說區別,先編譯。GoASM中編譯分兩步:
(1)  編譯:goasm /x64 1.asm
(2)  連結:golink 1.obj user32.dll
    如果一些正常,命令列中應顯示圖3的內容。這段代碼是在GoASM中編譯,指令部分GoASM與ML64差不多,關鍵是一些宏的定義有差別。比如masm中的.code,在這裡就成了CODE SECTION。下面再說區別,先編譯。GoASM中編譯分兩步:
(1)  編譯:goasm /x64 1.asm
(2)  連結:golink 1.obj user32.dll
    如果一些正常,命令列中應顯示圖3的內容。程式碼:;範例程式碼2.asm
;文法:ML64
extrn MessageBoxA: proc

.data
text     db 'Hello x64!', 0
caption  db 'My First x64 Application', 0

.code
Main proc
sub rsp,28h
xor r9d,r9d
lea r8, caption
lea rdx, text
xor rcx,rcx
call MessageBoxA
add rsp,28h
ret

Main ENDP
endml64 2.asm /link /subsystem:windows /entry:Main user32.lib。如果正常,應該很有意思吧,在64位系統下,我們仍然調用user32的API。可能是名稱用習慣了,微軟自己都懶得改了吧。

3.2  64位的椎棧
    代碼中還有一處值得注意,那就是sub rsp,28h和add rsp,28h。28h這個數值是怎麼來的呢?
首先,x64中椎棧被擴充為64位;其次,我們在調用MessageBoxA時,要給四個參數外加一個返回地址留空間,因此8(位)*5=40=28h。
    另外一些小問題要注意,AMD64不支援push 32bit寄存器的指令,最好的方法就是push和pop都用64位寄存器。EM64T如何?看了下Intel的開發手冊,各個指令都分三種情況:純32位、純64位和32與64位混合。下面是手冊的片段:

Opcode*      Instruction        64-Bit Mode       Compat/Leg Mode      Description
FF /6         PUSH r/m16         Valid                 Valid            Push r/m16.
FF /6         PUSH r/m32         N.E.                  Valid            Push r/m32.
FF /6         PUSH r/m64         Valid                  N.E.           Push r/m64.

Default operand size 64-bits.

    沒別的好方法,使用中多注意,盡量在64位程式中保用64位寄存器。

4.  一些參考資料
    寫完了第一個hello world,本文就此打住。本還想寫一些內容,但掌握不深,留待下回吧。感覺有些資料不得不在第一篇文章中放出來,因為它們是現有學習x64彙編的最好教材了,文中很多代碼和知識點也來自於這些資料。
(1)《Moving to Windows x64》,出自:http://www.ntcore.com/Files/vista_x64.htm
(2)GoASM的協助文檔,目前最好的64位彙編教程。出自:www.jorgon.freeserve.co.uk
(3)《開始進行 64 位元 Windows 系統編程之前需要瞭解的所有資訊》,出自:http://www.microsoft.com/china/MSDN/library/Windev/64bit/issuesx64.mspx
(4)來自CodeGurus的兩篇文章
《Assembler & Win64》,

http://www.codegurus.be/codegurus/Programming/assembler&win64_en.htm

《bout RIP relative addressing》

http://www.codegurus.be/codegurus/Programming/riprelativeaddressing_en.htm

(5)AMD開發手冊
(6)Intel開發手冊,注意是新的《ntel 64 and IA-32 Architectures software Developer’s Manual》

    64位技術現在還不成熟,沒有好調試器,但是我們搞技術的總是對新東西充滿了好奇和熱情。這個理由就足夠我們現在開始學習64位彙編了!OK,Let’s go on。

1.  再說Calling convention
    關於API的調用方式,在入門(1)中說了一些,不過感覺有必要再講兩點。一是在調用API時椎棧的架構,也就是Stack Frame,二是利用反組譯碼64位C/C++程式來研究calling convention。
    先說Stack Frame。圖1是一個通用的椎棧架構。
在一個使用STDCALL的32位程式中,stack frame的四項工作:
(1)  傳入參數的調用;
(2)  在返回caller時,callee要負責平衡椎棧;
(3)  給局部變數提供空間;
(4)  保證ebx、esi、edi和ebp四個寄存器的值不變(這種寄存器被稱為non-volatile)。
在64位環境中,少了一個平衡椎棧的任務,因為平衡椎棧的工作由caller負責了,因此callee的stack frame只剩下三項工作:
(1)  將寄存器傳入的參數和其它超過4個以上的參數在椎棧上儲存(入棧);
(2)  給局部變數提供空間;
(3)  保證non-volatile寄存器的值不變,包括ebp、ebx、rdi、rsi、r12到r15,xmm6到xmm15。

    所以,在一個函數的開始往往有如下代碼:

  MOV [RSP+8h],RCX
  MOV [RSP+10h],RDX
  MOV [RSP+18h],R8
  MOV [RSP+20h],R9
  PUSH RBP
  MOV RBP,RSP

    而在返回時會有如下代碼:
  LEA RSP,[RBP]
  POP RBP
  RET

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.