1. 程式人生 > >64位匯編第一講——64位寄存器環境和編譯環境20171229

64位匯編第一講——64位寄存器環境和編譯環境20171229

都是 containe 不同 log 就會 col 操作 data 字節數

一.64位匯編的歷史淵源 Intel公司和AMD公司都是研發復雜指令集的公司,AMD公司整體實力比Intel公司差一些,一直以來都是Intel公司的產品主導市場,在研發64位CPU時,為了提高CPU效率,Intel公司對之前版本的CPU指令進行了大改,研發出安騰CPU IA64_CPU,這款CPU雖然效率高,但不兼容之前的版本,所以並不被市場接受,而與此同時AMD公司研發出了能兼容之前版本的64位CPU,稱為AMD64 CPU,這款CPU雖然不如安騰CPU效率高,但卻因為兼容老版本而占據了市場,無奈Intel公司只得重新研發能兼容老版本的64位CPU,但為了有自己的特色,一些指令和AMD64 CPU有些不同,因為64位CPU名稱很多,所以一般都會統稱為X64 CPU。後來Intel公司研發了能支持一些安騰CPU的AMD庫類CPU,這款CPU兼容老版本,同時還提高了一些效率,目前是64位匯編主流。 二.64位匯編的編譯環境 微軟是支持Intel公司為提高效率而對CPU大改的,因為這樣的話,微軟產品的效率也會大大提高,相應於安騰CPU,微軟也研發好了相應的編譯器。對於其它版本的64位CPU,微軟的新產品自然也會支持,像VS2013是支持64位CPU的,所以可以從VS2013的安裝目錄下找到支持64位匯編的編譯器ml64.exe和鏈接器link.exe。要方便使用編譯器和鏈接器,自然得添加環境變量,不過在Win——所有程序——VS2013——VS tools中便會有很多快捷方式,其中“VS2013 x64 本機工具命令提示”便可用做命令行來編譯64位的*.asm文件和鏈接64位的*.obj文件,至於文件夾之間的切換,同cmd中操作。另外GoASM也可用於編譯和鏈接x64位程序,效率相對VS2013要高一些。微軟的編譯器和鏈接器為X64位逆向首選,GoASM為64開發首選。 三.64位匯編寄存器 64位匯編中寄存器除了段寄存器外,其余的都是64位,即8字節,所以棧結構的入棧和出棧字節數都要求模8。相比32位匯編,64位匯編的通用寄存器在數量上多了8個,共有16個通用寄存器,其中八個是兼容32位匯編的,分別是將原來的名稱e**改成了r**,如eax改成rax,其余8個分別命名為R8、R9、……R15,EIP和EFlags都改成RIP和RFlags,高32位都是0.,浮點寄存器還是64位,於32位匯編中一樣,分別稱為MMX0(或記為FPR0)、……、MMX7(或記為FPR7)。另外,還增加了16個128位的多媒體寄存器——XMM0、……XMM15,俗稱SSE指令,XMM0等多媒體指令又是256位寄存器YMM0等的低128位,這些多媒體寄存器的出現可以是得float型數據計算非常快,一次算四個,相當於原來的兩倍,廣泛應用於遊戲、視頻和音樂中。用於調試64位程序的調試器有WndDbg和X64Dbg。rax等初始8個通用寄存器,取其中的低32位、第16位、第8位分別用相應的寄存器取便是,如rax分別是eax、ax、al,R8等後來按序號命名的寄存器取64位、低32位、低16位、低8位分別用R8、R8D、R8W、R8B等。 四.64位匯編寫彈Hello World!窗口實例 先看匯編代碼如下:
include user32.inc
includelib User32.lib ;extern MessageBoxA:Proc 如果包了*.inc頭文件,則該條指針可註釋掉 .data g_szContent db "Hello World!",0 g_szTitle db "title",0 .code Winmain Proc sub rsp, 28h xor rcx, rcx mov rdx, offset g_szContent mov r8, offset g_szTitle xor r9, r9 call MessageBoxA add
rsp, 28h ret Winmain Endp end

在64位編譯命令下,指令如下:

ml64 /c Hello.asm 
link /subsystem:windows /entry:Main Hello.obj
  對於以上代碼,可以發現64位中參數傳參方式有些不同,那到底是怎樣呢?64位CPU中通用寄存器多,所以函數的頭四個參數都是寄存器傳參,再有多的才用棧傳參,一般函數的參數在四個以下的居多,所以大多數的函數調用時都用不上棧傳參了,傳參的四個寄存器統一規定依次是rcx、rdx、r8、r9,如此便不用如棧傳參時先入棧右邊,第5個寄存器之後用棧傳,仍是由先往後開始入棧,入棧指令不用push,而改用mov。64位匯編中不使用32會匯編中的任何調用約定,只按以上說的調用方法,有點像_fastcall,但是如果有用到棧傳參,則都是調用函數方平棧,這也是為了迎合不定參的統一平棧方式。 如以上程序代碼中,沒有用到棧傳參,那為何還要在函數頭部擡棧,函數尾部減棧呢?這也是為了在需要用到很多寄存器時,存放rcx、rdx、r8、r9的值,以便充分利用寄存器,當然不一定每次都會存放,只有在用其中的寄存器值時,就會將其中的值存到騰出的棧中,在整個函數中,只需頭部和尾部做一次操作就好,只要擡棧和減棧的數量相等就可。其實如果僅僅保存rcx、rdx、r8、r9的值,0x20字節的空間就夠,之所以要騰0x28的空間,是因為有的函數內部會使用多媒體寄存器,那是16字節的,所以要求每個函數中使用的棧大小還得模16,一個函數中調用另一個函數最初始是要壓入調用函數的返回地址的,需占用8個字節,如果加上擡的0x20字節,那麽被調函數中將是從0x28字節開始,0x28不模16,如果擡棧0x28,則0x28 + 8 = 0x30模16,即0x10,所以擡棧的數量一般是模8,不模16,在大的項目中函數頭部一般擡0x28的棧即可,如果是擡0x38自然也行,只是有些讓費,如果是擡0x18,則就需要求本函數內調的函數只能分別是兩個參數了,實為不妥。 註意當函數內擡了棧後,獲得參數和局部變量時,都得從原來的地址加上泰德棧字節數,如下:
MyAdd proc
  
sub rsp, 28h mov [rsp+30h], ecx ;原本第一個參數地址該是rsp+8h,再加28h,變成rsp+30h mov [rsp+38h], edx mov eax, ecx add eax, edx add rsp, 28h ret MyAdd endp
五. 64位匯編和32位匯編除了上面的還有那些區別 64位匯編中聲明和定義時都不需要寫參數了(對於四個參數或四個參數以下的是,五個或五個參數以上的有如何就不太明白)。64位匯編只需寫區,定義變量就好,不需要如32位匯編中頭部的一些聲明。64位匯編鏈接時需要指定程序入口,而32位匯編則不用,源於函數定義上一些不同,如下:
;64位匯編
.code
Main proc 
  ret
Main endp
end
;32位匯編
.code
start:
Main proc 
  ret
Main endp
end start
32位匯編在代碼中就已通過end start指定程序入口就是start,64位匯編中沒有,所以在鏈接時需指定程序入口。

64位匯編第一講——64位寄存器環境和編譯環境20171229