数据宽度:事实上就是数据所占用的存储空间。
原码:最高位为符号位,其余位为数值本身的绝对值。
反码:正数的反码和原码相同。负数反码符号位为1,其余取反。
补码:正数的补码和原码相同。负数的补码相当于先取补码再+1。
无符号数的编码规则:即存多少就是多少,如1001 1010 即为9A。
有符号数的编码规则:最高位为1代表负数,最高位为0代表正数。
计算机只能通过位运算实现运算。
与运算:两个位均为1时,结果才为1。
或运算:只要有一个为1就是1
异或运算:不一样的时候是1(xor)
非运算(单目运算): 即取反
左移:全部左移若干位,高位丢弃,低位补0(shl)
右移:全部右移若干位,低位丢弃,高位补零(shr)或符号位(sar)
通过位运算实现四则运算
加:
1)进行异或(不考虑进位的情况下,异或得到的结果和加相同)
2)判断是否有进位(通过与运算实现)
3)与得到的数左移一位再与1)得到的结果继续异或
重复这几个过程,直到与运算得到的结果为0000 0000,则已得出最终结
减:
做减法过程与做加法相同,只不过减数符号位从0变成了1
而乘除在本质上是和加减一致的
通用寄存器(32位)
eax ebx ecx edx
esp ebp eip edi
标志寄存器
efl(32位) 每个位的含意不同
其第10位是DF位,当DF位为0,执行完movs指令(以及类似指令如stos),edi和edi的值+1/2/4,取决于操作了几个字节,当DF位为1时,为-1/2/4
bit 0
CF[carry flag] 若算数操作产生的结果在最高有效位发生进位或借位(减法)则将其置1,反之清零
通常用来指示无符号整型运算的溢出状态
bit 2
PF[parity flag] 如果结果的最低有效字节包含偶数个1位则置1,否则清零
利用PF可进行奇偶校验检查
bit 4
AF[auxiliary carry flag]
如果算术操作在结果的 第3位发生进位或错位则将该标志置1,否则清零
在BCD(binary-code decimal)算数运算中被使用
bit 6
ZF[zero flag]
若结果为0则置1,反之清零
常与cmp(相当于sub,但是不储存结果,只影响标志寄存器)或test(相当于and,同样只影响标志寄存器)等指令一起使用
bit 7
SF[sign flag]
被设置为有符号整型的最高有效位,0为正1为负
bit 11
OF[overflow flag]
溢出标志OF用于反应有符号数加减运算所得结果是否溢出
判断溢出,无符号数运算看CF位
有符号数运算看OF位
bit 10
DF[direction flag]
std以及cld指令分别用于设置(置1)以及清除DF标志
内存
汇编指令中要将数据写入内存时,必须要指明数据宽度,mov byte ptr dr:[0x41f00c],1
大端模式:数据高位在地址低位,数据低位在地址高位
小端模式:数据低位在地址低位,数据高位在地址高位
常用指令
以下r代表通用寄存器,m代表内存,imm代表立即数,r8代表8位通用寄存器,以此类推
mov
注意数据宽度对齐
1.mov r/m,r
2.mov r,imm
add
1. add r/m,imm
2.add r/m,r
3.add r,r/m
其运算结果均储存在第一个操作目标
sub
与add类似,不过是在做减法
and
1.and r/m,imm
2.and r/m,r
3.and r,r/m
运算结果同样放在第一个操作目标处
xor
1.xor r/m,imm
2.xor r/m,r
3.xor r,r/m
not
not r/m(单目运算)
movs
移动数据内存-内存
movs byte ptr es:[edi],byte ptr ds:[esi] 简写为movsb
于此类似还有 movsw(数据宽度为word) movsd(数据宽度为dword)
stos 将ax/ax/eax的值存储到 [edi]指定的内存单元
stosb stosw stosd
rep
重复次数取决于ecx中的值
mov ecx,10
rep movsd 每执行一次ecx中值会-1
push
push r/m/imm
1.向堆栈中压入数据
2.自动修改栈顶指针esp的值
本质上可以通过mov,sub等指令合成
pop
栈顶值出栈,eip+4
jmp
jmp r/m/imm
修改eip的值,控制程序执行流
call
call r/m/imm
1.修改eip值(相当于jmp)
2.call下一个栈帧地址(高地址)压栈(相当于push)(目的是保存父函数eip的值)
leave
1.mov esp,ebp(返回父函数前将子函数栈顶esp抬高至ebp处)
2.pop ebp(此时esp指向的是prev_ebp,ebp被恢复为父栈底)
ret
1.栈顶指针指向的值存入eip(leave结束后首先将esp抬高,然后此时esp指向ret_addr,即父函数的eip),通过此指令将eip恢复
2.esp+4(32位),至此父函数状态得以恢复
jcc
类似jmp,但是否修改eip取决于标志寄存器的值
如jz,若z位为0,则不修改eip
内存
堆栈:不需要主动申请,kernel自动分配
esp:栈顶指针寄存器,存储的是使用过的栈最低地址(由高地址向低地址增长)
eip:储存下一条执行地址
函数
指令的集合
执行函数:函数调用
1.jmp
2.call
通常用eax存放返回值,可以用寄存器存放参数,也可以通过堆栈传参,其值的取用通过取用esp+4*n实现
堆栈平衡
esp寻址:
即函数调用前后栈顶(esp)应不变
1.由于函数内部修改esp的值(如push)导致ret时esp不是原栈顶,函数内通过pop push入的值平衡
2.堆栈传参导致的,通过外平栈(在返回位置加入修改esp值指令)或内平栈(ret n 相当于返回时esp+n)平衡(传递完毕的参数不应该是栈顶)
ebp寻址:
call函数,call会将ret_addr(即call下一条指令压栈),之后由于要进入调用函数的栈帧,所以首先得push ebp保存父函数的栈底(高地址),然后再将ebp降低到esp的位置(栈顶指针不需要保存,因为栈帧相邻,而且父函数栈顶必定是子函数的栈顶),这步通过mov ebp,esp实现,之后再将esp-n则创立了一个新的栈帧。