1.这是我根据我的印象抽取的重点,希望不要奶死了。
2.里面有看汇编代码的迅速上手,如果要熟练,请一定要完成几个书上的汇编逆向出代码的题目。
-
当表达式既有signed又有unsigned的时候,自动转换为unsigned
-
数的补码表示:最高位为符号位,代表的值不再是2^k 而是 -2^k,但是其余位不变。
eg.
[1001] = -8 + 0 + 0 + 1 = -7; -
负数补码算数右移,加偏置量2^n-1(n为右移位数)
eg.
[1001] >> 2 = [1110] = -8 + 4 + 2 + 0 = -2;
而 -7 / 4 = -1;❌
所以要加偏置量2^2 - 1 = 3 = [0011];
则[1001] + [0011] = [1100];
[1100] >> 2 = [1111] = -8 + 4 + 2 + 1 = -1;✔️ -
二进制小数:只能表示x/(2^k)
eg.
[0.101] = (4 + 1) / (2 ^ 3) = 5 / 8; -
浮点数:非规格化的作用
1、提供了一种表示0的方法,因为规格化数小数位>=0。
eg.-0.0 & 0.0,除了符号位为1 & 0 其余全为0
2.提供一种叫逐渐溢出的属性,即0.0周围的小数都分布均匀地接近于0 -
浮点数:偏置量bias = 2^(k - 1)-1 ;(k为浮点数指数位位数)
-
浮点数:非规格化数的偏置为什么为1-bias,而不是bias
因为规格化数小数位隐含第一位为1,而非规格化数没有。
为了实现最大非规格化数与最小规格化数之间的平滑过度,所以为1-bias -
浮点数:特殊值
if(指数位全为1)
{
if(若小数位全为0)
表示∞
else
否则表示NaN
} -
寄存器
要看懂汇编,先要清楚寄存器。
常用的寄存器有:ax,bx,cx,dx,si,di,bp,sp(具体作用,请结合我另一文章看)
这些都是16位的寄存器。
(括号内英文仅为方便记忆)
如果加上l(low),说明为低8位,为8位寄存器。如al,bl,sil,spl
如果加上h(high),说明为高8位,仍然为8位寄存器。如ah,bh
如果加上e(enough足够),说明为32位寄存器,如eax,esi,edi
如果加上r(redundant超多),说明为64位寄存器,如rax,rsi,rdi
//是不是与c语言的union类型比较像
1 -
指令:
一般的指令有mov,jmp,push之类的,后面一般有表示操作数后缀。
2
比如movl表示要移动4*8=32位的数据到寄存器内,所以指令与你操作的数据以及寄存器要匹配得上。
eg. movl %esi,%al
这就不匹配,l说明移动32位,但%al寄存器为8位寄存器,无法容纳。 -
取指针的值
有指针 p,p存在寄存器esi中,但esi存的只是p的值,是地址。
如果要获取p的值,要通过括号(%esi)来获取。 -
复杂取地址
记住,a(b,c,d)在外面的,以及非第二个逗号两边的树外,都是直接相加。
比如 movl 12(%ebp),%eax ->%eax = %ebp + 12;
而第二个逗号左右两边的数,为乘。
比如 movl 12(%esi,%ebp,6),%eax -> %eax = %ebp * 6 + %esi + 12; -
控制指令
要条件跳转,先要判断。
判断指令有cmp,test
test一般用法是 test a,a ->看a是否为0
cmp是最常用的
比如 cmp a,b
就做b - a的运算,并且将结果暂存。
接着就需要条件跳转语句jmp
j代表跳转
后面可以接以下字母以及他们的组合
g(great): >
a (above) : >
e (equal) : =
l (less) : <
b (below) : <
eg组合:jle .L1(如果less equal <= 则跳转到L1)
-
while的三种翻译方法
1、do-while
2、跳到中间的翻译方法
3、gaurded-do翻译方法(做了优化) -
条件传送指令
cmov
后缀与jmp一样,此外还多了
s(signed) 负数
ns(not signed)非负数
用法
test x,x
comvns a,b
如果x不为负数,(则取前面的)即为a = a
若x为负数,则 a = b
//类似于C语言 a>=0?a:b;
- 复杂数据类型的表示
数组:在内存中连续存储
//此时就可以联系到前面说过的取址指令了
比如数组int a[3];
a为数组首地址,每个数组元素占4Bytes
若a存在寄存器%edi中,索引i存在%esi中
那么取a的元素,放到%eax(用于存返回值的寄存器)
a[i] : movl (%edi,%esi,4), %eax;
//若为double数组,改4为8即可
struct结构类型
同理可按照数据大小进行连续排列
eg
strcut test
{
char c;
int I;
double d;
}
那么 那么d就在test偏移为5(sizeof int 和 char)的地方。
但是,在有一些操作系统中,会有数据对齐的要求。
要求:一个数据的首地址应该为该数据大小的整数倍,结尾也是。
因此上述例子就不正确了,因为char占1B
i就不能紧接着c存储,而必须空出3位,到从地址为4的地方开始到8结束(地址从0开始计算),而8正好为double的倍数,所以可以直接开始存储d。
此时d相对于test偏移量就不再是5了,而是8。
3
- 缓冲区溢出攻击
利用数据读取漏洞,通过注入溢出的数据代码,使得程序运行一些黑客所需要的代码。
现代操作系统对缓冲区溢出攻击的防范机制:
4
-
代码优化方法
1、消除循环低效率
2、减少过程调用
3、消除不必要的内存引用 -
评估程序性能
标准:CPE(每元素周期数)
表示处理器性能的参数
延迟:完成运算所需的时间
发射时间:两个连续通类型的运算之间所需要的最小周期数
容量:能够执行该运算的功能单元数量
/*
大概可以理解为,一辆赛车有氮气加速,延迟代表一罐氮气喷完所需时间,发射时间代表,一罐氮气的充气时间,容量代表你有几个气罐子。
*/
eg
一款处理器
加法:延迟:3周期,发射时间:1周期,容量:2
需要做两个加法运算
如果两个加法相互独立,正好有容量为2,那么他们可以同时运算,则CPE = 3
如果,两个加法之间有数据依赖:比如 a = b + a; a = a + a;那么必须先等第一个做完(花费3周期),再另外一个做(花费3周期)总共是6周期。
//注意这里加法单元容量为2
如果容量改为1:
先等第一个做完(花费3周期),准备(花费发射1周期),再另外一个做(花费3周期)总共是7周期。
网友评论