Windows X64汇编入门

本文介绍64位汇编的基础知识,包括开发环境搭建、寄存器使用及调用约定等,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近断断续续接触了些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
end
ml64 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值