汇编笔记
基本指令操作
EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI 32位通用寄存器
r8 代表8位寄存器(byte)
m16 代表16位内存(word)
imm 代表立即数
sub 减 add 加
mov eax,dword ptr ds:[0x12ff03] 把内存12ff03里面的值取出来给寄存器eax
lea eax,dword ptr ds:[0x12ff03] 把内存12ff03这个地址取出来给寄存器eax (eax=0012ff03)
堆栈
用mov,lea,sub来代替push
一个地址拥有的数据宽度是八位,一个字节
为了方便用DWORD,一次存储四个字节
假设EDX栈顶,EBX栈底
MOV EBX,19FF24
MOV EDX,19FF24
方法一:
LEA EDX,DWORD PTR DS:[EDX-4]
MOV DWORD PTR DS:[EDX],0xAAAAAAAA
=PUSH 0xAAAAAAAA
方法二:
MOV DWORD PTR DS:[EDX-4],0xBBBBBBBB
SUB EDX,4
方法三:
SUB EDX,4
MOV DWORD PTR DS:[EDX],0xEEEEEEEE
分别使用栈底加偏移、栈顶加偏移的方式读一个数,并存储到寄存器中.
选择EAX存储
MOV EAX,DWORD PTR DS:[EDX+4]
MOV EAX,DWORD PTR DS:[EBX-4]
用add,mov,lea来代替pop
MOV EDI,DWORD PTR DS:[EDX]
ADD EDX,4
MOV ESI,DWORD PTR DS:[EDX]
LEA EDX,DWORD PTR DS:[EDX+4]
POP EAX 将ESP的值赋给EAX,再将ESP+4
例题:实现push esp
方法一
MOV DWORD PTR DS:[ESP-4],ESP
SUB ESP,4
方法二
MOV DWORD PTR DS:[ESP-4],ESP
LEA ESP,DWORD PTR DS:[ESP-4]
PUSH r32
PUSH r16
PUSH m16
PUSH m32
不能push 8位 ,push 16位ESP减2
pop r32
pop r16
pop m16
pop m32
不能pop 8位 ,pop 16位ESP加2
PUSHAD
把八个通用寄存器按顺序都压入内存里 ESP-32
POPAD
把八个通用寄存器都取出来,ESP+32
标志寄存器(EFLAGS寄存器)
CF:如果最高位进位或借位为1,否则0
一个问题:什么叫最高位借位
其实和最高位进位一样,如80+80
1000 0000
+ 1000 0000
----------------------------
最高位进位
80-81
1000 0000
- 1000 0001
-----------------------------
最高位被借了,指的都是最前面的1的前面那个数
PF:如果最低有效字节“1”的个数为偶数,则PF的值为1,否则其值为0。
AF:如果数据宽度为DWORD,则看第4位是否进位或者借位,有1,无0
如果数据宽度为WORD,则看第2位是否进位或者借位,有1,无0
如果数据宽度为WORD,则看第1位是否进位或者借位,有1,无0
(16进制看)
ZF:恰好全为0的时候为1,其余为0
SF:第一位为1就为1,第一位为0就为0
OF:有符号运算溢出为1,否为0
正 + 正 = 正 如果结果是负数,则说明有溢出
负 + 负 = 负 如果结果是正数,则说明有溢出
正 + 负 永远都不会有溢出.
计算机如何知道是否进位?
如1000 0000
+ 1100 0000
------------------------
符号位有进位(第一位):1
最高有效数值向符号位产生的进位(第二位):0
1 xor 0 ==1 所以OF=1
一些指令
ADC 指令:带进位加法
与普通加法ADD不一样的是加一次就多加一个1 如1+2=4 2+3=6
SBB指令:带进位减法
与普通减法SUB不一样的是加一次就多加一个1 如3-1=1 9-4=4
XCHG指令:交换
MOVS指令:内存之间的移动
把寄存器esi所存的地址里面的数据复制到edi
简写:MOVSD,MOVSW,MOVSB
MOVS 完了之后 改变的数据宽度为几就加/减几,如DWORD +/-4 WORD +/-2
具体是加还是减看标志寄存器DF(direction)方向
DF=1为减,0为加
STOS指令
简写:STOSD,STOSW,STOSB
MOV BYTE/WORD/DWORD PTR ES:[EDI]
将al/ax/eax的值赋给EDI
具体是al还是ax还是eax
由BYTE/WORD/DWORD决定
STOS 完了之后 改变的数据宽度为几就加/减几,如DWORD +/-4 WORD +/-2
方向由DF决定,为1就是把EDI指向的地址减去,为0就是加上
REP指令
重复执行n次,n为计数寄存器(ECX)的值
REP MOVSD
REP STOSB
2.只让CF为1:
MOV AL,0FE
ADD AL,10
3.只让PF为1:
MOV AL,55
ADD AL,1
4.只让AF为1:
MOV AL,3F
ADD AL,1
5.只让SF为1:
MOV AL,0EE
ADD AL,1
6.只让OF为1:
只能让两个负数相加,正数相加SF位肯定为1
但最小的两个数80+80都让CF 溢出,为1
所以无法满足
JMP指令
唯一影响EIP(寄存器,内存都不变),相当于是MOV EIP一个值,但是不允许这么写
是专有操控eip的,可以理解成给eip赋一个一值
写成JMP 寄存器/地址
call指令
也是改变EIP,相当于MOV EIP,但是ESP会发生变化,ESP里面存的是返回地址
那么计算机怎么知道他要回哪?
是根据当前的位置+指令占得字节数
图中401000是当前的地址,加上5
也就是后面显示的五个字节,所以下面一步的地址是401005,这里要用到硬编码的知识
然后看到这边
栈顶变了,压入了栈里4字节,来存放返回的地址
ret 指令
相当于POP EIP,ret了之后就回到了返回地址,并且将堆栈里存的返回地址pop出去
ESP重新回到19FF74,但是不是删除,只是返回地址在堆栈外面了
比较指令
CMP指令:
指令格式:CMP R/M,R/M/IMM
该指令是比较两个操作数,实际上,它相当于SUB指令,但是相减的结构并不保存到第一个操作数中,
而是观察标志寄存器中的ZF位,为1则说明相等
那么比较大小的话怎么办呢?
还是观察标志寄存器,SF,为1说明前面那个数比后面的小,为0则大
TEST指令:
指令格式:TEST R/M,R/M/IMM
相当于and(与)操作,但也是不改变操作说
更多的用处是判断某一个寄存器是不是空的,是否为0
jcc
jcc就是JMP,只是在一些条件下JMP
本质是jcc观察标志寄存器某些位的值
JE, JZ 结果为零则跳转(相等时跳转) ZF=1
JNE, JNZ 结果不为零则跳转(不相等时跳转) ZF=0
JS 结果为负则跳转 SF=1
JNS结果为非负则跳转 SF=0
JP, JPE 结果中1的个数为偶数则跳转 PF=1
JNP, JPO 结果中1的个数为偶数则跳转 PF=0
JO 结果溢出了则跳转 OF=1
JNO 结果没有溢出则跳转 OF=0
JB, JNAE 小于则跳转 (无符号数) CF=1
JNB, JAE 大于等于则跳转 (无符号数) CF=0
JBE, JNA 小于等于则跳转 (无符号数) CF=1 or ZF=1
JNBE, JA 大于则跳转(无符号数) CF=0 and ZF=0
JL, JNGE 小于则跳转 (有符号数) SF≠ OF
JNL, JGE 大于等于则跳转 (有符号数) SF=OF
JLE, JNG 小于等于则跳转 (有符号数) ZF=1 or SF≠ OF
JNLE, JG 大于则跳转(有符号数) ZF=0 and SF=OF