栈区讲解🍊
由于最开始已经讲过Push这里我们直接给图
大家不要太在乎esi,edi有什么用,只要知道是寄存器就行。
通过上面的图,我们已经知道了,esp已经移动到了一个比较远的地方,那么我们的栈顶当然也随之增高了。
现在我们要压ebx,esi,edi
当然由于是一次压一个寄存器,所以,esp会移动三次,这里我们就直接画出最终的结果。
lea🍊
在汇编语言中,lea(Load Effective Address)是一条指令,用于将一个内存地址的有效地址(即计算出的地址)加载到一个寄存器中。它的语法通常是:
lea destination, source
其中,destination表示目标寄存器,source表示源操作数,可以是一个内存地址或者一个寄存器。
eg:
将数组arr的第3个元素的地址加载到寄存器ebx中:
lea ebx, [arr+2 * 4]
这里的arr+2 * 4表示数组arr的起始地址加上2个元素的偏移量,乘以每个元素的大小(4字节)。
那么也就是说将[ebp - 4Ch]这个地址加载到edi里面,
我们之前就算过4Ch就是76,ebp我们很早就压在栈区,
这当中肯定有一种设计不然不会这么巧妙~
我们给出图。
为什么为局部变量的值是随机值?🍊
来看这行代码,现在我们再来看我们的栈区
发现好像没有ecx和eax啊,因为我们并没有push(压栈)但这里其实并不需要把这两个压在栈里。
这两个move很好理解,就是把后面的十六进制放到这两个对应的寄存器中,13h=19(十进制) ,而这个0XCCCCCCCC是不是有点像我们没有初始化的时候数组里面放的随机值。
那到底是怎么放进去的呢?
我们看最后一个代码
我们来介绍一下这行代码
rep stos是一条汇编指令,用于重复执行字符串存储操作。它的语法通常是:
rep stos destination
其中,destination表示目标操作数,通常是一个内存地址。
dword ptr是一个操作数大小的修饰符,用于指定操作数的大小为双字(32位)。word是两个自己,double word就是4个字节,刚好我们的寄存器就是4个字节(32位)
[edi]是一个寻址方式,表示通过寄存器edi中的地址作为目标操作数。
因此,指令rep stos dword ptr [edi]的含义是重复执行将32位数据存储到地址edi指向的内存位置的操作。
具体操作的次数由寄存器ecx中的计数值决定。在执行rep stos之前,通常会将计数值存储到ecx寄存器中。
我们看看edi中的地址在哪里
也就是我们main函数开始的地方,然后从下初始化ecx次CCCCCCCC就行了,为什么ecx里是19(十进制)呢?因为19 *4 =76。
那么eax呢?不是要把0xCCCCCCCC放进去吗?但是这个地方我们不需要管这么多,只要知道这三行汇编语言是初始化一个函数栈帧就行了。
这就是为什么我们的局部变量不初始化就会变成0xCCCCCCCC的原因。
开始执行代码🍊
dword ptr是一个操作数大小的修饰符,用于指定操作数的大小为双字(32位)。word是两个自己,double word就是4个字节,刚好我们的寄存器就是4个字节(32位)
int a = 10 :
我们看到了mov,也就是说把0A这个16进制数放到ebp-8这个地址上,0A转换为十进制就是10,ebp-4是哪个地方的地址?
我们直接看图
而我们对比一下在VS 2022中的汇编代码
也就是说在VS2022要隔4个字节,但是过程基本是一致的。
然后我们给出我们现在栈区的图解
函数调用前的准备(就是传参)🍊
由于前面已经讲的很清楚了,这里我们就简单说一下,
把ebp-8地址的值引用然后,放到eax里,再push eax,
同理ecx也一样。
我们给出图解
这个过程其实就是相当于我们的函数传参。