1.寄存器
x64系统通用寄存器名称,第一个字母从“E”改为“R”,数量增加了8个,(R8-R15),增加了8个128位XMM寄存器(XMM寄存器用来优化代码)
用表格示意:
说明:
2.栈平衡
栈的基本入栈出栈操作和32位一样,需要注意就是在64位环境下,汇编指令对栈顶的对其值有要求,因此在VS编译器申请空间的时候,会尽力保证栈顶地址为对其值16.(被16整除) 在逆向中发现栈空间不使用的情况,可能为了实现对齐
3.调用约定
64位应用程序只有1种寄存器快速调用,前4个参数使用寄存器传递,超过4个参数就放到栈里,入栈的顺序是从右往左,
前四个寄存器是固定的 RCX,RDX,R8,R9.
所有大于8字节的,不是1.2.4.8字节的参数都由引用来传递
所有的浮点参数的传递都是使用XMM寄存器完成,他们在XMM0,XMM1,XMM2,XMM3中传递
例如:如果存在一个函数为 void fun(float,int float,int)
那么参数传递顺序第一个参数(XMM0),第二个参数(RDX) 第三个参数(XMM2) 第四个参数(R9)
函数的前4个参数虽然使用寄存器来传递,但是栈还会为这4个参数保留空间(32字节) 这里称为预留栈空间
为什么要这么做? 因为我们使用寄存器传参的缺点是如果我们的函数调用较为复杂,寄存器会存在不够用的情况(4个用来传参了)这是就会利用栈空间暂时保存寄存器的数值,当函数调用完毕再由调用者恢复堆栈。
4.64位的数据结构
局部变量
例如函数入口指令为sub rsp,30h.从rsp+0h到rsp+20h为32字节预留栈空间,rsp+20h到rsp+30h为局部变量空间,也等于预留栈空间在低地址,局部变量空间在高地址
局部变量通常都在栈中保存,如果是Release编译并寄存器够用,局部变量可能会在寄存器中保存
全局变量
全局变量的地址在编译器时候就固定了,先定义的在低地址,后定义的在高地址。根据这个特征可以确定定义顺序
数组
数组的结构相对来说是较为“整齐”,数组中的数据在内存中的存储是线性连续的,从低地址到高地址的存储。
Ps:int型数据在64位系统下还是32位的大小,如果要用64位则可以用long 或者 __int64
访问数组的时候就是数组首地址+数据类型大小*下标
控制语句
if语句和32位一样,不多赘述
switch-case效率高于if 。switch分支数小于6时,会直接用if else来实现,大于等于6时候会进行编译器优化。优化方式就是case的值间隔比较小就会采用case表的方式去调用case内容
当case的内容较多的时候,就需要用二叉树来讲case比较次数进行优化(折中比较,逐渐缩小范围)
64位下转移指令机器码的计算
JMP: 和32位的计算方式相同:
位移量=目的地址-起始地址-跳转指令的长度
转移指令机器码=转移类别机器码+位移量 (JMP xxxx E9 xx)
CALL: call的使用方式和32位下略有不同
32位下例如
CALL dword ptr ds:[42413C] 对应的机器码就是 FF15 3C414200.这里的43413C是绝对地址
64位就是相对地址:
相对地址=14000B388-1400018CB-指令长度=9ABD-6=9AB7
机器码=FF15+相对地址=FF157B79A0000