这一章主要是要理解汇编代码,以及如何将高级语言转换为汇编语言
一个x86-64中央处理器包含一组16个存储64位值的通用寄存器
有不同的寻址方式
寄存器可传送6个整形参数
%rdi %rsi %rdx %rcx %r8 %r9
7~n的参数存放在栈上,对于寄存器不足够存放所有的本地数据,局部变量使用“&”或是数组结构必须存放在内存里
【举例1】
swap_add(&arg1, &arg2)
long swap_add(long *xp, long *yp)
调用swap_add,先将arg1,arg2放进栈里,再将参数地址放进寄存器里
subp $16, %rsp //先将栈指针减6
movq $12, (%rsp) //向上存放参数
movq $34, 8(%rsp)
leap 8(%rsp), %rsi //将参数地址放进寄存器里
movq %rsp, %rdi
call swap_add
【举例2】
long call_proc(){
long x1 = 1;
int x2 = 2;
short x3 = 3;
char x4 = 4;
proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4);
return (x1+x2) * (x3-x4);
}
要为有地址引用的参数分配空间,以及超出寄存器的部分也要存放在内存中,从底向上依次存放参数7~n,局部变量,被保存的寄存器,返回地址。其栈帧分配如下图
%rbx, %rbp, %r12~%r15为被调用者保存寄存器,若应用程序后会用到被调用者保存寄存器,应在汇编开头先push
循环语句主要是有两种翻译方案,一是跳转到中间,另一个是guarded_do翻译方案
while(n > 1){
result *= n;
n = n-1;
}
goto test;
loop:
result *= n;
n = n-1;
test:
if(n > 1)
goto loop;
首先使用条件分支,若初始条件不成立就跳过循环
if(!t)
goto done;
loop:
....
....
if(t)
goto loop;
done: return;
struct {
char *a;
double c;
char d;
int h;
char f;
};
注意最后还要填充到32字节满足8字节对齐,这样才能保证p->a,p->c的地址都是8的倍数
结构体节省空间的办法就是重新排列将字节数从大到小排列
long vframe(long n, long idx, long *q){
long i;
long *p[n];
p[0] = &i;
for(i = 1; i < n; i++)
p[i] = q;
return *p[idx];
}
分配地址空间如下
leaq 22(,%rdi,8), %rax //8n+22
andq $-16, %rax //向下约到16的倍数
suq %rax, %rsp //若n为奇数,%rsp向下增加8n+8,n为偶数为8n+16
leaq 7(%rsp), %rax
shrq $3, %rax //向上舍入到最近的8的倍数
leaq 0(,%rax,8), %r8
movq %r8, %cx
s2-s1为8n+8或8n+16,p为离s2最近的8的倍数
是输入字符串里包含可执行代码的字节编码,或是用一个指向攻击代码的指针覆盖返回地址
栈随机化
栈破坏检测
限制可执行代码区域
CF:进位标志
ZF:零标志
SF:符号标志
OF:溢出
AF:辅助进位
PF:奇偶位
OF可以表示带符号数的运算是否超出相应的数值范围
NOT不修改任何的状态标志
INC,DEC不影响CF
leaq,乘法,除法指令不改变条件码