X64 汇编入门

目录

前言

64位和32位汇编的差异

示例说明

 

前言

公司项目需要实现通过汇编来获取调用栈的功能,自己写了一个,直接崩溃,回头学习一下,以此为记;

64位和32位汇编的差异

这方面我现在涉及的不多,就不展开了,大家可以自己去查一下资料,或者直接参考一下下面的链接:

https://blog.csdn.net/qq_29343201/article/details/51278798

示例说明

重头戏来了,先上C源码:

#include <stdio.h>
#include <stdlib.h>
//gcc test.c -o test -g

int SumFunc(int a, int b)
{
	int sum = 0;
	sum = a + b;
	return sum;
}

int main(int argc, char* argv[])
{
    int a = 3;
    int b = 4;
    int sum = 0;
    sum = SumFunc(a, b);
    printf("sum = %d\n",sum);

    return 0;
}

编译后,通过objdump看一下汇编代码:

注意AT&T和Intel的汇编的差异,后面不再说了;

开始GDB之:

前面几步没啥好说的,主要是关注RBP、RSP、RIP这3个,然后直接到关键点:

输入si[注意一定是si,不能是ni],执行单条汇编命令跳入函数,也就是执行call 0x40052d,注意RSP、RIP以及stack的变化:

发生变化的寄存器:

1、RIP

变化是正常的,RIP每一步都会变换;但是注意SumFunc里的push rbp还没有执行——这是下一个要执行的指令——那么变化了的RIP到底执行了什么?

2、RSP

原来:

现在:

——这里有点奇怪!

首先看一下RSP当前指向的是啥:

可以看到,RSP入栈了main函数里call SumFunc后的下一条指令——这就很清楚了,这是保护现场,用于在SumFunc完成调用后继续执行main流程的下一步;

所以,RSP减少了 0x7fffffffe170 - 0x7fffffffe168 = 8[这里注意不是10-8=2,而是(F+1)-8 = 8],就是RIP寄存器的大小(8字节),等于是把一条汇编指令入栈;

同时可以得到这个结论:

在汇编代码里,call 一个函数的时候,其实是调用了 push RIP;

下面继续:

从上面的截图也可以看到,进入SumFunc后,前两条汇编是:

一般进入每个函数的开始都是这两条;下面来分析一下这两条指令做了什么;

1. push rbp

很明显,就是把rbp入栈;rbp里保存的是什么呢?

rbp指向的是:

从开始到现在,rbp的值还没有变化过,用下面的图可以简单表示:

——上面这个截图有个错误,就是edi应该是占4个字节,rdi才是8字节,edi是指rdi的低32位;

在进入SumFunc时,执行第一句 push rbp前,当前的栈,以及各个寄存器的情况:

现在我们执行push rbp:

可以发现,入栈后,rbp的值没有变化,还是190,而rsp的值又减小了8——符合预期,现在的栈的情况:

在GDB调试界面上输入ni,执行mov rbp, rsp,就是把rsp的值赋给rbp,rsp的值不发生变化,而rbp从原来的190变为160:

然后继续执行,下面两句汇编是从edi和esi中获取入参,放入rbp相应的位置上,供后面相加使用:

——这里有一点需要注意:在64位汇编中,当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。在本例中就是:

简单来说就是把原来存储入参的寄存器的值转移到eax和edx中,然后进行add的操作;在执行完add操作后,会把sum写入到rbp-0x4中;

再执行pop rbp的操作,在上述过程中,rsp和rbp一直没有发生变化,都是160,并且rbp在rsp的栈顶:

 

rbp和rsp均发生了变化:

1.rsp:

pop是做出栈的动作,所以rsp应该是增加8,变为168

2.rbp:

pop rbp,对于rbp而言,应该是rbp = pop(rsp),等于是把栈顶的值赋给rbp

这里注意:0x7fffffffe160是栈顶的地址,其对应的值是0x7fffffffe190,所以pop后,rbp的值是0x7fffffffe190;

 

再执行ret,回到main函数内,注意rsp和rip的变化

可以看到rsp做了pop的动作,并且把pop的值赋给了rip,所以通过这里可以得到结论:

ret ====> pop rip

接下来就是调用printf函数打印的过程了,中间涉及一点,就是调用SumFunc时,返回的返回值是保存在eax里的——64位汇编里,函数返回值结果一般保存在rax或者eax(rax的低32位)中;

另外需要注意,在3和4时,rbp已经发生了变化,但对整个分析没有影响,因为结果是放在eax中的,使用eax进行函数结果的返回;

而edi中存储的则是printf函数的第一个参数:

至此,整个过程可以说告一段落;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lqw198421

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值