您当前的位置: 首页 > 

插件开发

暂无认证

  • 0浏览

    0关注

    492博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

X64位-汇编语言-汇编补丁-参数传递-堆栈对齐

插件开发 发布时间:2022-05-10 09:39:57 ,浏览量:0

文章目录
    • 1.32位指令区别
    • 2.64位指令文件执行
    • 3.64位调用约定
    • 4.当调用子函数时,堆栈指针RSP必须在16字节边界对齐上
    • 5.子函数开始预留空间计算
    • 6.总结
  进行任意软件的汇编补丁开发,需要对汇编语言有一定程度的了解,本人在补丁开发过程中,经常遇到各类问题,究其根本还是源于对汇编的一些规则了解不够,本文整理X64位汇编,为大家做参考。

1.32位指令区别

  64位指令集是在32位指令集上的扩展和发展,EIP扩展为RIP,EFLAGS扩展为RFLAGS。浮点栈寄存器没有变化,多媒体寄存器多个8个寄存器。如下图所示: 在这里插入图片描述   64位模式下对通用寄存器的高低位访问,对于通用寄存器分别在8位,16位,32位和64位下其访问的名称,这里主要重点提一下R8-R15的寄存器,分别在尾部加入B,W,D来表示高低位,(B - byte //1字节,W - word //2字节,D - dword //4字节)如下图所示: 在这里插入图片描述   如果操作数是8位或者16位,其高位不修改,当使用32位时则会零扩展,也就是高4字节默认会被影响修改为0:

zero-extend for 32-bit operands   //32位操作数使用了零扩展
not modified for 16-bit operands  //16位操作不影响
not modified for 8-bit operands   //8位操作不影响
2.64位指令文件执行

  比如在VS2015 64位命令行环境,输入如下指令,进行编译和链接:

ml64 /c HelloWord64.asm
link /subsystem:windows /entry:start HelloWord64.obj
3.64位调用约定

  使用四寄存器的fastcall调用约定,堆栈平衡由调用者处理,同时为了保证程序的正常运行,需要预留32(4*8)字节的空间,如果函数内部,其寄存器如果不够用了,那么一定会需要将寄存器中的参数保存到堆栈中。

RCX - 参数一
RDX - 参数二
R8  - 参数三
R9  - 参数四
剩余的参数依次入栈
返回值使用RAX寄存器

  函数内部:

sub rsp,20h  ;开辟空间用于保存参数
mov [rsp+0],rcx
mov [rsp+8],rdx
mov [rsp+10h],r8
mov [rsp+18h],r9
 
;.... 函数内部逻辑
add rsp,20h

  相对于自己开辟空间保存寄存器,这里rcx保存的位置是[rsp+8],因为[rsp+0]的位置的返回地址。当有函数调用时,必须要提供这个预留空间(就算没有参数传递),否则不遵守这个规则,程序有空崩溃,因为函数调用者不提供这个预留空间,当函数内部有使用时很容易就蹦,没使用就又会一切正常。

4.当调用子函数时,堆栈指针RSP必须在16字节边界对齐上

  64位CPU规定,把一个n字节的数据放到栈里面,栈地址必须模n,而现在16字节的话意味着需要模16,如何判断一个地址是模16呢,其实就看最低位是不是0就可以。每进入一个函数,Call指令自动抬了一个8位空间用于保存返回指令,再抬一个8位空间,RSP就是16位对齐了,如果不这么遵守,那么当遇到多媒体指令的时候就很容易蹦了。 在这里插入图片描述

5.子函数开始预留空间计算

  以六参数为例,预留空间是0x20字节,额外参数空间0x10字节,临时变量参数空间0x10字节,如下所示:

预留空间       0x20字节
调用参数空间   0x10字节  //预留空间4个参数,如果某函数最多需要传递6个参数,那么还需开辟2个参数的空间
局部参数空间   0x10字节  //两个参数
-------------------------
              0x40 字节  -对齐->  0x48字节

  参数五和参数六,这里不能使用push来压栈,因为这里使用push来压栈,那么其参数数值就会在预留空间的上面,而我们需要的是在预留空间的下面,所以此时只能自己手动进行mov。 在这里插入图片描述

extern MessageBoxA:proc
extern ExitProcess:proc
 
includelib user32.lib
includelib kernel32.lib
 
.data
body db 'Hello World!',0
capt db 'x64',0
 
.code
start proc
	sub rsp,8     ;;将堆栈指令对齐到偶数16字节边界
	sub rsp,20h
	xor r9d,r9d   ;参数四
	lea r8,capt   ;参数三
	lea rdx,body  ;参数二
	xor ecx,ecx   ;参数一
	call MessageBoxA
	add rsp,28h
	xor ecx,ecx
	call ExitProcess	
start endp
end
6.总结
1.传递给函数的前四个参数放在RCX,RDX,R8和R9寄存器中
2.调用者的职责是在运行时分配至少32字节的预留空间,以便调用的函数可以选择在此区域保存寄存器参数
3.当调用子函数时,堆栈指针RSP必须在16字节边界对齐上

  本文参考https://blog.csdn.net/Wang_1997/article/details/106718533   合理的脚本代码可以有效的提高工作效率,减少重复劳动。

关注
打赏
1665481431
查看更多评论
立即登录/注册

微信扫码登录

0.0427s