到目前位置,我们了解的到内存寻址最简单的方法就是移位寻址,但对于分析C/C++一些复杂的代码片段更底层操作原理,仅靠前文那些汇编知识是不够的。
通过上一篇的学习,我们《x86汇编基础-move指令和基本寻址》,我们已经知道在
- C代码中每声明一个基本数据类型的变量都映射到一个寄存器中。
- 而寄存器则是在内存和CPU之间交换数据的一个“高速中转站”
- 并且我们也引入了“加载”和“存储”
- 也引入了两个基本内存寻址概念“间接引用”和“移位寻址”
移位寻址的一般形式“D(Rb,Ri,K)”等价于“RAM[Reg[Rb]+K*Reg[Ri]+D]”其中:
- D是移位常量("Displacement"),可以是1,2或4字节。
- Rb是基址寄存器(Base Register),可以是任何8/16位整数的寄存器。
- Ri是索引寄存器(Index Register),除了%esp(或%rsp)和%ebp (或%rbp)之外,其他任意的寄存器。
- K是一个2的倍数,可以是1,2,4或8等.
移位寻址的特殊形式
- (Rb,Ri):最终的内存地址是RAM[Reg[Rb]+Reg[Ri]]
- D(Rb,Ri):内存地址的计算形式就是RAM[Reg[Rb]+Reg[Ri]+D],这里的2的倍数K就是1
- (Rb,Ri,S):内存地址的计算形式是RAM[Reg[Rb]+K*Reg[Ri]]
内存寻址计算示例:
在x64中有一条指令叫lea的指令,当然x86的版本是leal,指令的一般形式如下,该指令的操作数可以是上面提过的表达式形式
- leal src,dst
- src源地址模式的表达式
- dst目标地址模式的表达式
例如:leal (%exx,%ecx,4), %eax 就表示%eax=%edx+4*%ecx,
- 不需要内存引用直接计算内存地址。例如C代码中int *p=&x[i];
- 计算的算数表达式形式是x+k*i,其中k可以是1,2,4,或8.
为什么是k只能是这些数字?你可以从C角度去考虑,char是1字节的整数是基本数据类型里面最小的数据长度,double/long int是8字节是目前基本数据类型中支持最大的数据长度。
算数运算符
在反编译的C函数的上下文中,经常会碰到lea指令和以下算术操作符
指令一起出现,因此我们回顾一下
这里着重所以下imull指令,实质上执行的就是位移运算的指令,因此乘法运算只需位移运算即可。
- sall向左位移,并且保留了符号位,获得源操作数后向左移动指定的位数,并将其结果保存回目标寄存器。
- sarl向右位移,并且保留了符号位,获得源操作数后向右移动指定的位数,并将其结果保存回目标寄存器。
- shrl向右移动,但不会对符号位做任何操作。
- subl,xorl指令和orl指令需要注意参数的顺序,因为参数的顺序会影响计算的结果,
- 符号和无符号整数之间没有区别的,如果你选择补码,则可以不考虑符号位的情况下进行算术运算,所有操作的位都紧随其后,但需要注意,由于符号位的存在,你确实需要留意自己执行的是自动转换还是逻辑转换转换其他一些算术运算
网友评论