Windows X86-64位汇编语言入门Windows X64汇编入门(1)最近断断续续接触了些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上这种用法会与某些指令产生冲突。
3. 第一个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的操作:代码:这段代码是在GoASM中编译,指令部分GoASM与ML64差不多,关键是一些宏的定义有差别。
比如masm中的.code,在这里就成了CODE SECTION。
下面再说区别,先编译。
GoASM 中编译分两步:(1)编译:goasm /x64 1.asm(2)链接:golink 1.obj user32.dll如果一些正常,命令行中应显示图3的内容。
运行一下,我们的第一个64位windows程序就运行了。
GoASM还有一个特点是支持宏:ARG和INVOKE,使用这两个宏可以免除程序员自己对椎栈进行操作。
不过初学吗,还是从基础掌握比较好。
下面的一段代码相同的功能的MASM代码,注意看看区别。
ML64至今仍不支持宏,所以每一步工作都要自己做。
代码:编译这段代码的命令行是:ml64 2.asm /link /subsystem:windows /entry:Main user32.lib。
如果正常,应该如图5显示那样。
很有意思吧,在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 Descrip tionFF /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位程序中保用644. 一些参考资料写完了第一个hello world,本文就此打住。
本还想写一些内容,但掌握不深,留待下回吧。
感觉有些资料不得不在第一篇文章中放出来,因为它们是现有学习x64汇编的最好教材了,文中很多代码和知识点也来自于这些资料。
(1)《Moving to Windows x64》,出自:/Files/vista_x64.htm (2)GoASM的帮助文档,目前最好的64位汇编教程。
出自:(3)《开始进行 64 位 Windows 系统编程之前需要了解的所有信息》,出自:/china/MSDN/library/Windev/64bit/issues x64.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》Windows X64汇编入门(2)五一长假就要结束了,总算有时间好好睡了几个懒觉。
今天醒来后想到的第一件事就是,该写第二篇了。
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],RCXMOV [RSP+10h],RDXMOV [RSP+18h],R8MOV [RSP+20h],R9PUSH RBPMOV RBP,RSP而在返回时会有如下代码:LEA RSP,[RBP]POP RBPRET图2摘自GoASM的帮助文档,上文描述的情况在图中一目了然。
如果能在VC中编译64位C/C++程序,再用IDA反汇编,不是挺好的吗?正确,这正是我们玩儿逆向工程的人喜欢的方法。
Visual Studio 2005的64位开发环境设置网上有,这里不多说了。
以一个C/C++的代码为例:代码:这段代码是一个地球人都知道的窗口消息处理代码,在编译为64位程序后,用ida64看一下它的反汇编。
这样,熟悉而又有点陌生的64位汇编代码就出来了,包括消息的判断,EndDialog的调用等,确实很方便。
2. 第二个汇编例子:SMC在入门(1)中我们写了第一个64位的汇编程序,这里我们开始写第二个。
当然,代码本身还是有点意思的,这就是Self Modify Code。
让我们试一试SMC在64位下进行的如何?这还牵涉到vista的特性。
代码来自修改过的参考资料《About RIP relative addressing》。