IDA系列--x64逆向

基础知识

函数调用约定

Windows
  • 整数和浮点数参数

32位cdecl、stdcall、fastcall等几种,但64位统一为一种变形的fastcall。64位的fastcall中最多可以把函数的4个参数存储到寄存器中传递:

参数整数型浮点数型
1stRCXXMM0
2stRDXXMM1
3stR8XMM2
4stR9XMM2

超过4个参数,使用栈来传递,传递顺序依照“从右向左”。此外,函数返回时传递的参数过程中所用的栈由调用者清理。看上去64位的fastcall就像32位下的cdcel和fastcall的结合。函数的前4个参数虽然使用寄存器传递,但在栈中仍为这4个参数预留了空间(32个字节)。

Note: 当整数和浮点数参数混合出现时,
eg: void func(float a, int b, double c, int d);
a放入XMM0中,b放入RDX,c放入XMM2,d放入R9。
这个存放的顺序很怪异,其实这是严格按照表中整数和浮点数的4个参数一一对应,需要为没用的参数预留空间,比如c参数没有放到XMM1中,XMM1被预留位置了。

  • 指针参数

指针参数的传递遵循整数参数传递的方式。

  • 结构体参数
    结构体参数比较特殊,如果结构体长度小于64bit,则使用整数参数的传递规则。但如果是一个很大的结构体,那么应该还是要在堆栈中申请临时空间的(但ddk没有明说这一点,参考x86的规则应该如此)。

  • 未声明函数调用

func1();
func2()
{
    func1(2, 1.0, 7);
}

在这种情况下,func1()的参数表其实不明确,那么参数的传递要怎样进行?这里采用了一个比较保守的规则,就是:整数参数还是按照寄存器映射关系放入对应的寄存器中,浮点数在按照映射关系放入XMM寄存器后,还需要按照整数参数的寄存器映射关系放入整数寄存器中一次,这就是“比较保守的规则”的意思。就现在这个例子而言,结果如下:2在RCX中,1.0在RDX和XMM1中,7在R8中。

细节

  • Windows 的 x64 下只有一种函数调用约定,即 __fastcall ,其他调用约定的关键字会被忽略,也就是说 ABI 只有 __fastcall ;
  • 一个函数在调用时,前四个参数是从左至右依次存放于 RCX、RDX、R8、R9 寄存器里面,剩下的参数通过栈传递,从右至左顺序入栈;
  • 如果是 int f(double a, double b, double c, double d, double e, double f) 这样的函数,前四个浮点类型参数从左到右由 XMM0,XMM1,XMM2,XMM3 依次传递,剩下的参数通过栈传递,从右至左顺序入栈;
  • 调用者负责在栈上分配32字节的“shadow space”,用于存放那四个存放调用参数的寄存器的值(亦即前四个调用参数);
  • 小于64位(bit)的参数传递时高位并不填充零(例如只传递ecx),也就是说结构体或union如果大小是1,2,4,8字节,用值传递(相应的寄存器),大于8字节(64位)必须按照地址(指针)传递;
  • 被调用函数的返回值是64位以内(包括64位)的整形或指针时,则返回值会被存放于RAX;
  • 如果返回值是浮点值,则返回值存放在XMM0;
  • 更大的返回值(比如结构体),由调用方在栈上分配空间,并由 RCX 持有该空间的指针并传递给被调用函数,因此整型参数使用的寄存器依次右移一格,实际只可以利用 RDX,R8,R9,3个寄存器,其余参数通过栈传递。函数调用结束后,RAX 返回该空间的指针(即函数调用开始时的 RCX 值)。
  • 调用者 (caller) 负责清理栈,被调用函数 (callee) 不用清栈,可是为什么有时候我们看到调用者 (caller) 也没有清栈呢?后面会讲;
  • 除了 RCX,RDX,R8,R9 以外,RAX,R10,R11 和 XMM5,XMM6 也是“易挥发”的,不用特别保护,其余寄存器需要保护。(x86下只有 eax, ecx, edx 是易挥发的)
  • 栈需要16字节对齐,“call”指令会入栈一个8字节的函数返回地址(函数调用指令后的下一个指令的地址)(注:即函数调用前原来的RIP指令寄存器的值),这样一来,栈就对不齐了(因为RCX、RDX、R8、R9四个寄存器刚好是32个字节,是16字节对齐的,现在多出来了8个字节)。所以,所有非叶子结点调用的函数,都必须调整栈RSP的地址为16n+8,来使栈对齐。
  • 对于 R8~R15 寄存器,我们可以使用 r8, r8d, r8w, r8b 分别代表 r8 寄存器的64位、低32位、低16位和低8位。
Linux

细节

调用约定细节:

  • Linux 下的调用约定叫做 “System V AMD64 ABI”,此约定主要在 Solaris,GNU/Linux,FreeBSD 和其他非微软OS上使用;
  • Linux 的 x64 下也只有一种函数调用约定,即 __fastcall ,其他调用约定的关键字会被忽略,也就是说 ABI 只有 __fastcall ;
  • 一个函数在调用时,如果参数个数小于等于 6 个时,前 6 个参数是从左至右依次存放于 RDI,RSI,RDX,RCX,R8,R9 寄存器里面,剩下的参数通过栈传递,从右至左顺序入栈;
  • 如果参数个数大于 6 个时,前 5 个参数是从左至右依次存放于 RDI,RSI,RDX,RCX,RAX 寄存器里面,剩下的参数通过栈传递,从右至左顺序入栈;
  • 对于系统调用,使用 R10 代替 RCX;
  • XMM0 ~ XMM7 用于传递浮点参数;
  • 小于64位(bit)的参数传递时高位并不填充零(例如只传递ecx),也就是说结构体或union如果大小是1,2,4,8字节,用值传递(相应的寄存器),大于8字节(64位)必须按照地址(指针)传递;
  • 被调用函数的返回值是64位以内(包括64位)的整形或指针时,则返回值会被存放于 RAX,如果返回值是128位的,则高64位放入 RDX;
  • 如果返回值是浮点值,则返回值存放在XMM0;
    更大的返回值(比如结构体),由调用方在栈上分配空间,并由 RCX 持有该空间的指针并传递给被调用函数,因此整型参数使用的寄存器依次右移一格,实际只可以利用 RDI,RSI,RDX,R8,R9,5个寄存器,其余参数通过栈传递。函数调用结束后,RAX 返回该空间的指针(即函数调用开始时的 RCX 值)。
  • 可选地,被调函数推入 RBP,以使 caller-return-rip 在其上方8个字节,并将 RBP 设置为已保存的 RBP 的地址。这允许遍历现有堆栈帧,通过指定GCC的 -fomit-frame-pointer 选项可以消除此问题。
  • 调用者 (caller) 负责清理栈,被调用函数 (callee) 不用清栈;
  • 除了 RDI,RSI,RDX,RCX,R8,R9 以外,RAX,R10,R11 也是“易挥发”的,不用特别保护,其余寄存器需要保护。
  • 在调用 call 指令之前,必须保证堆栈是16字节对齐的;
  • 对于 R8~R15 寄存器,我们可以使用 r8, r8d, r8w, r8b 分别代表 r8 寄存器的64位、低32位、低16位和低8位。
字节大小

数据类型和大小

取决于是64位编译模式还是32位编译模式(注意,和机器位数没有直接关系)

在64位编译模式下,指针的占用内存大小是8字节
在32位编译模式下,指针占用内存大小是4字节

DWORD 4个字节

寄存器基础

通用寄存器 (general register)

通用寄存器(general-purpose registers, GPRs) ,每一个用户空间的程序,或者内核程序都用到的,基本的寄存器。
因为X86-64是从32位的X86,甚至16位、8位演变而来的,为了软件可以向前兼容,所以,这些寄存器都有不同的版本。话不多说,看下表:

64-bit32-bit16-bit8-bit (Low / High)
RAXEAXAXAL / AH
RBXEBXBXBL / BH
RCXECXCXCL / CH
RDXEDXDXDL / DH
RSIESISISIL
RDIEDIDIDIL
RBPEBPBPBPL
RSPESPSPSPL
R8R8DR8WR8B
R9R9DR9WR9B
R10R10DR10WR10B
R11R11DR11WR11B
R12R12DR12WR12B
R13R13DR13WR13B
R14R14DR14WR14B
R15R15DR15WR15B

通用寄存器总共: 68.

案例分析

v8 = *(_DWORD *)(*a3 + 92i64);
i64为MS后缀,可直接去掉

movd xmm6, dword ptr [rax+38h]

  • 从地址[rax+38h]取出double word字节(32位)的数据存入xmm6的寄存器
  • movd: 移动双字
  • xmm6: 浮点寄存器
  • dword ptr: 双字指针

cvtsi2sd xmm1, dword ptr [rbx+11Ch]

  • cvtsi2sd: 将源操作数(第二个操作数)中的有符号双字整数转换成目标操作数(第一个操作数)中的双精度浮点值。源操作数可以是通用寄存器或 32 位内存位置。目标操作数是 XMM 寄存器。结果存储到目标操作数的低位四字,高位四字保持不变。

参考

x64 调用约定
Windows/Linux x64汇编函数调用约定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值